模板模板参数的模板参数推导错误

时间:2018-06-25 06:33:53

标签: c++ c++11 templates variadic-templates template-templates

我在编写非模板类的模板成员时遇到问题,尤其是在推导模板模板参数的模板参数时。

以下代码(最少)说明了我的问题。我怀疑那里至少存在两个不同的问题。

#include <iostream>
#include <list>

using namespace std;

struct A
{
    list<int> l;

    template<template<class> class Iterable> 
    A(Iterable<int>& it);
};

template<template<class> class Iterable>
A::A(Iterable<int>& it) : l(list<int>())
{
    for(int i : it)
        l.push_back(i);
}

int main()
{
    list<int> l = {1, 2, 3, 4};
    A a(l);
    for(int i : a.l)
        cout << i << " ";
     cout << endl;

    A b = {1, 2, 3, 4};
    for(int i : b.l)
        cout << i << " ";
    cout << endl;
}

备注:我真的很想在课堂之外提供定义。我还希望A的构造函数的原型能够与整数列表,整数向量以及初始化程序列表一起使用。

1 个答案:

答案 0 :(得分:0)

  

我怀疑那里至少存在两个不同的问题。

正确:我看到了三个不同的问题。

1)std::list需要两个模板参数;默认为第二个(std::allocator<T>,其中T是第一个模板参数);以及向量,双端队列,集合等对模板参数(有些是默认参数)的要求更高。

因此template<template<class> class>std::list和其他STL容器不匹配。

更笼统地说,我建议

template <template <typename...> class Iterable, typename ... Ts> 
A (Iterable<int, Ts...> & it);

2)A b = {1, 2, 3, 4};不起作用,因为您正在调用带有四个参数的构造函数,而只有带有一个参数的构造函数。因此,您必须使用以下内容来明确显示容器

A b = std::list<int>{1, 2, 3, 4};

鉴于您希望std::initializer_list作为默认容器,如果您接受将图形加倍({ { 1, 2, 3, 4 } }而不是{ 1, 2, 3, 4 },而是将单个参数传递给构造函数),则可以指出std::initializer_list(或其他容器,如果您愿意)作为默认Iterable

我的意思是...如果您按如下方式声明构造函数

   template <template <typename...> class Iterable = std::initializer_list,
             typename ... Ts> 
   A (Iterable<int, Ts...> const & it);

然后您可以将b初始化为

   A b {{1, 2, 3, 4}};

3)在模板构造函数中,签名Iterable<int, Ts...> & it接受l值引用,因此接受

list<int> l = {1, 2, 3, 4};
A a(l);

但不接受

A b = std::list<int>{1, 2, 3, 4};

因为std::list<int>{1, 2, 3, 4}是一个r值。

要解决此问题,您可以为r值引用编写其他构造函数,但是在这种情况下,我怀疑可以接受,只需修改模板构造以使其能够接受 const l-值引用

A (Iterable<int, Ts...> const & it); 
// .....................^^^^^

奖金(非主题)建议:如果您有兴趣接受STL容器,则可以使用l构造函数,它接受几个迭代器(begin(),{{1 }}),所以

end()

或者也是

template <template <typename...> class Iterable, typename ... Ts>
A::A (Iterable<int, Ts...> const & it) : l{it.cbegin(), it.cend()}
 { }

以下是您修改的代码

template <template <typename...> class Iterable, typename ... Ts>
A::A (Iterable<int, Ts...> const & it) : l{std::cbegin(it), std::cend(it)}
 { }