简体   繁体   中英

Template template argument deduction failure with GCC (works with MSVC)

I have the following reasonably simple function template:

template <class OrderedSetType, template<class> class SupersetType>
OrderedSetType f(const SupersetType<OrderedSetType>& superset)
{
    return OrderedSetType();
}

It's called like this:

f(std::vector<std::string>());

And the compiler fails to deduce the template parameter. The diagnostic message isn't particularly helpful:

<source>: In function 'int main()':

<source>:12:33: error: no matching function for call to 'f(std::vector<std::__cxx11::basic_string<char> >)'

     f(std::vector<std::string>());

                                 ^

<source>:5:16: note: candidate: template<class OrderedSetType, template<class> class SupersetType> OrderedSetType f(const SupersetType<OrderedSetType>&)

 OrderedSetType f(const SupersetType<OrderedSetType>& superset)

                ^

<source>:5:16: note:   template argument deduction/substitution failed:

<source>:12:33: note:   template parameters of a template template argument are inconsistent with other deduced template arguments

     f(std::vector<std::string>());

                                 ^

Why does the error occur? Happens with GCC 7.3 with -std=c++14 , does not happen with -std=c++17 . Which changes in the C++ 17 standard allowed for this code to compile? And can I make it compile for C++14?

Here's the live demo: https://godbolt.org/g/89BTzz

Specifying the template arguments explicitly doesn't help, by the way.

PS In the meantime, MSVC has no problems with this piece of code, but clang 5 and 6 cannot compile it even in C++17 mode. So either clang has a bug and fails to compile standard-compliant code, or GCC has a bug and successfully compiles code that it shouldn't (with -std=c++17 ).

Try with

template <template <typename...> class SupersetType,
          typename FirstT, typename ... OthersTs>
FirstT f (SupersetType<FirstT, OthersTs...> const & superset)
 { return FirstT{}; }

or also

template <template <typename...> class SupersetType, typename FirstT>
FirstT f (SupersetType<FirstT> const & superset)
 { return FirstT{}; }

The problem is that std::vector doesn't accept only a type but two; the second is an allocator with a default value.

So you have to take in count this problem.

Obviously you can write f() with a template-template parameter that accept only two types

template <template <typename, typename> class SupersetType,
          typename FirstT, typename SecondT>
FirstT f (SupersetType<FirstT, SecondT> const & superset)
 { return FirstT{}; }

but if you use a template parameter that accept a variadic list of types, you have a more flexible f() (that match more containers)

Which changes in the C++ 17 standard allowed for this code to compile?

You're declaring the template template parameter SupersetType contaning only one template parameter, but the template template argument std::vector<std::string> has two, ie std::string and the default template argument std::allocator<string> . Before C++17 they don't match and leads to error (then you have to make them match to solve the issue), since C++17 ( CWG 150 ) it's allowed; ie the default template arguments are allowed for a template template argument to match a template template parameter with fewer template parameters.

 template<class T> class A { /* ... */ }; template<class T, class U = T> class B { /* ... */ }; template <class ...Types> class C { /* ... */ }; template<template<class> class P> class X { /* ... */ }; X<A> xa; // OK X<B> xb; // OK in C++17 after CWG 150 // Error earlier: not an exact match X<C> xc; // OK in C++17 after CWG 150 // Error earlier: not an exact match 

While this doesn't provide an answer to your problem, it provide an alternative.

Remember that all standard container have a public type named value_type . That means you could easily skip the template template and only have something like

template<typename ContainerT>
typename ContainerT::value_type f(ContainerT const& superset)
{
    return typename ContainerT::value_type();
}

As long as your SupersetType follows the standard containers with a value_type member, it should work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM