Search code examples
c++c++14c++17template-argument-deductiontemplate-templates

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.

P. S. 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).


Solution

  • 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)