考虑以下代码段...
void boo(std::initializer_list<unsigned> l)
{
}
template <class T>
void foo(std::initializer_list<T> l)
{
//Even though T is convertable, initializer list is not...:-(
boo(move(l));
}
int main()
{
foo({1u,2u,3u}); //Compiles fine as expected
foo({1,2,3}); //Fails to compile at line 9... - could not convert...
return 0;
}
...我很惊讶initializer_list<int>
无法转换为initializer_list<unsigned>
,但事件虽然int转换为无符号。
我一直想知道以什么方式可以编写foo以允许转换。可以以某种方式解开具有错误类型的列表并重新创建具有正确类型的新列表吗?
答案 0 :(得分:3)
不。构造初始化列表需要编译时已知长度,并且使用初始化列表,而您没有编译时已知长度。
初始化列表旨在用于“面向用户”的操作,其中用户是您的界面的使用者。在内部使用它们不会很好。
您可以使用的方法是编写一个array_view<T>
类型,它包含一系列连续内存(初始化列表,向量,std数组或C数组都是此类示例) -view like way(包括begin,end,data,size,empty,front,back方法。迭代器是指针)。
然后从vector<T>&
,vector<std::remove_const_t<T>> const&
,initializer_list<std::remove_const_t<T>>
等隐含转换ctors。
void boo(array_view<const unsigned> l)
{
}
template <class T>
void foo(std::initializer_list<T> l)
{
boo(std::vector<unsigned>{l.begin(), l.end()});
}
根据unsigned
中的任何内容重新分配l
值的新缓冲区,并将其传递给boo
。 boo
消耗array_view<unsigned const>
,vector
可以转换为initializer_list
或unsigned
maybe_convert_list
。
然后我们可以写template <class T, class U, class...LowPrecidence>
std::vector<T> maybe_convert_list(std::initializer_list<U> l, LowPrecidence&&...)
{
return {l.begin(), l.end()};
}
template <class T>
std::initializer_list<T> maybe_convert_list(std::initializer_list<T> l)
{
return l;
}
template <class T>
void foo(std::initializer_list<T> l)
{
boo(maybe_convert_list<unsigned>(l));
}
:
initializer_list<unsigned>
或某些人。它只留下std::vector<unsigned>
。对于其他类型的列表,它会将其转换为cudaEventRecord()
。
答案 1 :(得分:2)
虽然Foo<T>
可转换为Foo<U>
,但课程T
无法转换为U
。对于编译器,模板实例化中的不同类型会产生不相关类型的实例。
因此,在您的情况下,foo({1,2,3})
会将T
推断为int
,因此foo
的参数的类型为initializer_list<int>
。然后,您尝试将其传递给boo
,这会将initializer_list<unsigned>
无关传递给initializer_list<int>
,因此编译错误。
您可以通过模板专业化来避免这种头痛,即专门为foo
类型unsigned
专门设置:
template<>
void foo<unsigned>(std::initializer_list<unsigned>)
{
// specialization here
}
答案 2 :(得分:2)
简而言之,这种转换无法完成。拥有std::initializer_list<int>
对象后,无法使用它来合成std::initializer_list<unsigned>
。你可以迭代它,但问题是关于大小的信息不是静态可用的,所以没有办法从一个std::initializer_list
对象生成一个用括号括起来的初始化器列表来构造一个不同的{{1}对象。
如果std::initializer_list
需要接收boo
,则std::initializer_list<unsigned>
应该具有foo
类型的参数。您可以将std::initializer_list<unsigned>
转换为{1, 2, 3}
。但是一旦你把它推断为std::initializer_list<unsigned>
,这种可能性就会消失。
答案 3 :(得分:2)
虽然您无法在编译期间解压缩初始化程序列表(以执行必要的转换),但您可以按照您希望的方式创建它。请考虑以下代码:
#include <initializer_list>
void boo(std::initializer_list<unsigned> l);
template <class... T>
void foo(T... l)
{
boo({static_cast<unsigned int>(l)...});
}
int main()
{
foo(1,2,3);
return 0;
}