C++ template's template failed to compile

Viewed 375

I've got this test program

#include<iostream>
#include<vector>
using namespace std;

template<template<class> class C, typename T>
void print(const C<T>& c){
    for(auto& e : c)cout<<e<<endl;
}
int main(){
    vector<int> v;
    print(v);
    return 0;
}

It fails to compile:

g++ m.cpp -std=c++11
m.cpp: In function ‘int main()’:
m.cpp:11:16: error: no matching function for call to ‘print(std::vector<int>&)’
        print(v);
                ^
m.cpp:6:6: note: candidate: template<template<class> class C, class T> void print(const C<T>&)
void print(const C<T>& c){
    ^~~~~
m.cpp:6:6: note:   template argument deduction/substitution failed:
m.cpp:11:16: note:   template parameters of a template template argument are inconsistent with other deduced template arguments
        print(v);
                ^

I changed print() signature from (const C& c) to (C& c), it still fails:

$ g++ m.cpp -std=c++11
m.cpp: In function ‘int main()’:
m.cpp:11:16: error: no matching function for call to ‘print(std::vector<int>&)’
        print(v);
                ^
m.cpp:6:6: note: candidate: template<template<class> class C, class T> void print(C<T>&)
void print(C<T>& c){
    ^~~~~
m.cpp:6:6: note:   template argument deduction/substitution failed:
m.cpp:11:16: note:   template parameters of a template template argument are inconsistent with other deduced template arguments
        print(v);
                ^

How to fix it?

2 Answers

Your compilation problem arises because your template template parameter C doesn't match the declaration of std::vector:

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

As you can see, std::vector has two template parameters, while your C has only one. But, note as well that the second parameter (class Allocator) has a default type argument. Since C++17, this is well-formed even the way you've written it, as it was added that template template parameters matching doesn't require specifying parameters for ones with default arguments like Allocator. But not all compilers are supporting this modification to the language spec -- see here live how Clang 6.0.0 refuses to compile your original snippet with C++17 enabled. Going for older versions of C++ (or just any version to date of Clang), this snippet is probably what you were aiming for:

template<template<class, class> class C, typename T, typename A>
void print(const C<T, A>& c){
    for(auto& e : c)cout<<e<<endl;
}

As here you specify the correct template signature of the type (std::vector) you're later instantiating print() with.


This would work too, regardless of C++17:

template<template<class...> class C, typename T>
void print(const C<T>& c){
    for(auto& e : c)cout<<e<<endl;
}

That said, note that, as vector<int> is already a fully instantiated type, this simpler version works just as well in the given scope of your snippet:

template<typename T>
void print(const T& c){
    for(auto& e : c)cout<<e<<endl;
}

I changed print() signature from (const C& c) to (C& c), it still fails:

This is probably the better practice in this case, as you're not modifying c inside of print(). However, this has nothing to do with your error.

std::vector contains two template parameters, while the template template parameter C is declared to contain only one. Anyway, your code would work fine with C++17; since C++17 (CWG 150), the default template arguments are allowed for a template template argument to match a template template parameter with fewer template parameters.

LIVE

Before C++17 you may apply parameter pack.

template<template<class...> class C, typename T>
void print(const C<T>& c){
    for(auto& e : c)cout<<e<<endl;
}

LIVE

Related