Search code examples
c++templatesc++17variadic-templatestemplate-argument-deduction

Substitution failure for template template argument


I want a helper function to instantiate a class for me. Currently it cannot compile in clang (though it compiles work in gcc), but I need it to work in clang as well. Currently I'm using clang version 6.0.0-1ubuntu2.

I'm not sure why it's failing, since gcc is able to detect the type. I tried doing stuff from this post and playing around with it for a while but I keep running into a wall. MCVE available, or you can try it on coliru here:

#include <vector>

using namespace std;

template <typename T, template <typename> typename Container>
struct SomeClass {
    SomeClass(const Container<T>& c) {
    }
};

template <typename T, template <typename> typename C>
inline auto make_some_class(const C<T>& container) {
    return SomeClass<T, C>(container);
}

int main() {
    vector<int> ints;

    auto stuff = make_some_class(ints);  
}

main.cpp:19:18: error: no matching function for call to 'make_some_class'

   auto stuff = make_some_class(ints);  

                ^~~~~~~~~~~~~~~

main.cpp:12:13: note: candidate template ignored: substitution failure [with T = int]: template template argument has different template parameters than its corresponding template template parameter

inline auto make_some_class(const C<T>& container) {

            ^

1 error generated.


Solution

  • Suggestion: try with

    #include <vector>
    
    template <template <typename...> typename Container, typename ... Ts>
    struct SomeClass {
        SomeClass(const Container<Ts...>& c) {
        }
    };
    
    template <template <typename...> typename C, typename ... Ts>
    inline auto make_some_class(const C<Ts...>& container) {
        return SomeClass<C, Ts...>(container);
    }
    
    int main() {
        std::vector<int> ints;
    
        auto stuff = make_some_class(ints);  
    }
    

    I mean... I suppose the problem is that std::vector isn't a container that receive one type template parameter; it's a container that receive two type template parameter (the second one with a default type: std::allocator<T> where T is the first one).

    So the suggestion is: make SomeClass more flexible and able to receive a container with an variadic list of template types of arguments

    template <typename...> typename Container
    

    and the corresponding list of template types

    typename ... Ts
    

    If you want a variadic list of arguments (Ts...) you need it in last position so you have to switch the position of Container and T (now Ts...): before Container and after the variadic list Ts...

    template <template <typename...> typename Container, typename ... Ts>
    struct SomeClass {
        SomeClass(const Container<Ts...>& c) {
        }
    };
    

    Not strictly required but, for uniformity, I suggest to rewrite make_some_class() in the same way (and, obviously, pass C before Ts... in the template parameters list).

    template <template <typename...> typename C, typename ... Ts>
    inline auto make_some_class(const C<Ts...>& container) {
        return SomeClass<C, Ts...>(container);
    }