Search code examples
c++templatestemplate-templatesdefault-template-argument

Syntax for define a template that takes a template


I wanted to create a type that holds a generic type type, that, itself, is a template with one argument. So, if my type is called C, it could be summarized like C<T<U>>.

So, I went for it:

#include <vector>

template <template<typename> class T>
class C
{
    // implementation here
};

int main() {
    C<std::vector<int>> myType;
}

And I am facing this error:

<source>:10:7: error: template argument for template template parameter must be a class template or type alias template
    C<std::vector<int>> myType;
      ^
1 error generated.
ASM generation compiler returned: 1

What is the correct way of force a template to take a type that itself, is a template that expects other template argument?

Live example.


Solution

  • You can specialize the class for the case you need and let it undefined otherwise

    #include <vector>
    
    template <typename  T>
    class C;
    
    template <template<typename> typename TT, typename U>
    class C<TT<U>>
    {
        // implementation here
    };
    
    int main() {
        C<std::vector<int>> myType;
        // C<int> myTypeWrong; // compile error
    }
    

    The above does not work before C++17 because std::vector has a default for its second template type parameter,

    template<
        class T,
        class Allocator = std::allocator<T>
    > class vector;
    

    and before C++17 the part = std::allocator<T> is ignored, as far as matching the std::vector template template argument with the TT template template parameter above, so std::vector<T, std::allocator<T>> never successfully match C<TT<U>>. (Same thing holds for template<typename, typename = void> struct V {}; as it holds for std::vector, clearly.)

    This, together with the workaround mentioned in a comment and based on the usage of a variadic template parameter for the template template parameter, is explained in C++ Templates - The Complete Guide 2nd edition, §12.3.4, page 197-198.

    As pointed out in another comment, Clang hasn't adopted the C++17 change to the mentioned part of the standard.