模板模板参数的模板参数推导错误
Deduction error of template parameters of a template template argument
我在编写非模板的模板成员时遇到问题 class,特别是在推导模板模板参数的模板参数时。
以下代码(最小)说明了我的问题。我怀疑那里至少有两个不同的问题。
#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;
}
备注:我真的很想提供 class 的定义。我还希望 A
的构造函数的原型能够处理整数列表、整数向量以及初始化列表。
I suspect there are at least two different problems down there.
正确:我看到三个不同的问题。
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
,在你的模板构造函数中,接受一个左值引用,所以接受
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 左值引用
A (Iterable<int, Ts...> const & it);
// .....................^^^^^
奖金(题外话)建议:如果您有兴趣接受 STL 容器,您可以使用接受几个迭代器的 l
构造函数来简化模板构造函数(begin()
,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)}
{ }
以下是您修改后的代码
#include <initializer_list>
#include <iostream>
#include <list>
struct A
{
std::list<int> l;
template <template <typename...> class Iterable = std::initializer_list,
typename ... Ts>
A (Iterable<int, Ts...> const & it);
};
template <template <typename...> class Iterable, typename ... Ts>
A::A (Iterable<int, Ts...> const & it) : l{std::cbegin(it), std::cend(it)}
{ }
int main ()
{
std::list<int> l {1, 2, 3, 4};
A a(l);
for (auto i : a.l)
std::cout << i << " ";
std::cout << std::endl;
A b {{1, 2, 3, 4}};
for (auto i : b.l)
std::cout << i << " ";
std::cout << std::endl;
}
我在编写非模板的模板成员时遇到问题 class,特别是在推导模板模板参数的模板参数时。
以下代码(最小)说明了我的问题。我怀疑那里至少有两个不同的问题。
#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;
}
备注:我真的很想提供 class 的定义。我还希望 A
的构造函数的原型能够处理整数列表、整数向量以及初始化列表。
I suspect there are at least two different problems down there.
正确:我看到三个不同的问题。
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
,在你的模板构造函数中,接受一个左值引用,所以接受
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 左值引用
A (Iterable<int, Ts...> const & it);
// .....................^^^^^
奖金(题外话)建议:如果您有兴趣接受 STL 容器,您可以使用接受几个迭代器的 l
构造函数来简化模板构造函数(begin()
,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)}
{ }
以下是您修改后的代码
#include <initializer_list>
#include <iostream>
#include <list>
struct A
{
std::list<int> l;
template <template <typename...> class Iterable = std::initializer_list,
typename ... Ts>
A (Iterable<int, Ts...> const & it);
};
template <template <typename...> class Iterable, typename ... Ts>
A::A (Iterable<int, Ts...> const & it) : l{std::cbegin(it), std::cend(it)}
{ }
int main ()
{
std::list<int> l {1, 2, 3, 4};
A a(l);
for (auto i : a.l)
std::cout << i << " ";
std::cout << std::endl;
A b {{1, 2, 3, 4}};
for (auto i : b.l)
std::cout << i << " ";
std::cout << std::endl;
}