'C++ template compile failed

I want write an simple demo, but it compile failed, I can't find the reason.

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


template <
    typename T,  
    template <typename W> typename  Container = std::vector 
>
class myclass
{
public:
    Container<T> myc;

public:
    void func();
    myclass() 
    {
        for (int i = 0; i < 10; ++i)
        {
            myc.push_back(i);  
        }
    }
};

template <
    typename T,
    template <typename W> typename  Container 
>
void myclass<T, Container>::func()
{
    cout << "good!" << endl;
}


int main()
{
   
    myclass<int, vector> mylistobj2; 
    mylistobj2.func();


    return 0;
}

the compile reason is that

tmp.cpp: In function ‘int main()’:
tmp.cpp:100:30: error: type/value mismatch at argument 2 in template parameter list for ‘template<class T, template<class W> class Container> class _nmsp1::myclass’
  100 |  _nmsp1::myclass<double, list> mylistobj2; 
      |                              ^
tmp.cpp:100:30: note:   expected a template of type ‘template<class W> class Container’, got ‘template<class _Tp, class _Alloc> class std::__cxx11::list’
tmp.cpp:101:13: error: request for member ‘func’ in ‘mylistobj2’, which is of non-class type ‘int’
  101 |  mylistobj2.func();


Solution 1:[1]

std::vector has two template parameters (the 2nd one has default argument), while the template template parameter Container is declared with only one template parameter. They don't match. You may apply parameter pack.

template <
    typename T,  
    template <typename...> typename  Container = std::vector 
>
class myclass
{
public:
    Container<T> myc;

public:
    void func();
    myclass() 
    {
        for (int i = 0; i < 10; ++i)
        {
            myc.push_back(i);  
        }
    }
};

template <
    typename T,
    template <typename...> typename  Container 
>
void myclass<T, Container>::func()
{
    cout << "good!" << endl;
}

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.

Solution 2:[2]

The problem is that prior to C++17

a template template argument had to be a template with parameters that exactly match the parameters of the template template parameter it substitutes with some exceptions related to variadic template parameters.

In your given example this means that since std::vector template of the standard library has more than one parameter: The second parameter (which describes an allocator) has a default value, but prior to C++17 this was not considered when matching std::vector to the Container parameter.

There are 2 ways to solve this.

Solution 1

One solution is to rewrite the class myclass declaration so that the Container parameter expects containers with two template parameters as shown below:

template <typename T,  
    template <typename W,typename Alloc = std::allocator<W>> typename  Container = std::vector> //note the second template parameter Alloc added here 

class myclass
{
public:
    Container<T> myc;

public:
    void func();
    myclass() 
    {
        for (int i = 0; i < 10; ++i)
        {
            myc.push_back(i);  
        }
    }
};

template <typename T,
    template <typename W, typename Alloc> typename  Container> //note the second template parameter here

void myclass<T, Container>::func()
{
    cout << "good!" << endl;
}

The output of the above modified program can be seen here.

Solution 2

You can make use of parameter pack to solve this problem.

template <typename T,  
    template <typename...> typename  Container = std::vector> //note we have parameter pack

class myclass
{
public:
    Container<T> myc;

public:
    void func();
    myclass() 
    {
        for (int i = 0; i < 10; ++i)
        {
            myc.push_back(i);  
        }
    }
};

template <typename T,
    template <typename...> typename  Container> //note we have parameter pack 

void myclass<T, Container>::func()
{
    cout << "good!" << endl;
}

The output of the above modified program can be seen here.

Note also that your program works without any modification with C++17.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 songyuanyao
Solution 2