我在编写非模板类的模板成员时遇到问题,尤其是在推导模板模板参数的模板参数时。
以下代码(最少)说明了我的问题。我怀疑那里至少存在两个不同的问题。
#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
的构造函数的原型能够与整数列表,整数向量以及初始化程序列表一起使用。
答案 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)}
{ }