initializer_list<T>无法转换为initializer_list<U>,但T可转换为U
initializer_list<T> can't convert to initializer_list<U>, but T convertable to U
考虑以下代码片段...
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 来允许转换。能否以某种方式解开类型错误的列表并重新创建类型正确的新列表?
A class 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
}
总之,这个转换是做不到的。一旦你有了一个std::initializer_list<int>
对象,就没有办法用它来合成一个std::initializer_list<unsigned>
。你可以遍历它,但问题是关于大小的信息不是静态可用的,所以没有办法从一个 std::initializer_list
对象生成一个用括号括起来的初始化器列表来构造一个不同的 std::initializer_list
对象。
如果boo
需要接收std::initializer_list<unsigned>
,那么foo
应该有一个std::initializer_list<unsigned>
类型的参数。您可以将 {1, 2, 3}
转换为 std::initializer_list<unsigned>
。但是一旦你将其推断为std::initializer_list<int>
,这种可能性就消失了。
没有。构建初始化列表需要编译时已知长度,而使用初始化列表则没有编译时已知长度。
初始化程序列表用于 "user-facing" 操作,其中用户是界面的消费者。像这样在内部使用它们效果不佳。
您可以使用的一种方法是编写一个 array_view<T>
类型来包装一系列连续内存(初始化列表、向量、std 数组或 C 数组都是这样的例子)类似范围视图的方式(具有开始、结束、数据、大小、空、前、后方法。迭代器是指针)。
然后给它从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()});
}
根据 l
中的内容重新分配 unsigned
值的新缓冲区,并将其传递给 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>
.
虽然您无法在编译期间解压缩初始化列表(以执行必要的转换),但您可以按照您希望的方式创建它。考虑以下代码:
#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;
}
考虑以下代码片段...
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 来允许转换。能否以某种方式解开类型错误的列表并重新创建类型正确的新列表?
A class 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
}
总之,这个转换是做不到的。一旦你有了一个std::initializer_list<int>
对象,就没有办法用它来合成一个std::initializer_list<unsigned>
。你可以遍历它,但问题是关于大小的信息不是静态可用的,所以没有办法从一个 std::initializer_list
对象生成一个用括号括起来的初始化器列表来构造一个不同的 std::initializer_list
对象。
如果boo
需要接收std::initializer_list<unsigned>
,那么foo
应该有一个std::initializer_list<unsigned>
类型的参数。您可以将 {1, 2, 3}
转换为 std::initializer_list<unsigned>
。但是一旦你将其推断为std::initializer_list<int>
,这种可能性就消失了。
没有。构建初始化列表需要编译时已知长度,而使用初始化列表则没有编译时已知长度。
初始化程序列表用于 "user-facing" 操作,其中用户是界面的消费者。像这样在内部使用它们效果不佳。
您可以使用的一种方法是编写一个 array_view<T>
类型来包装一系列连续内存(初始化列表、向量、std 数组或 C 数组都是这样的例子)类似范围视图的方式(具有开始、结束、数据、大小、空、前、后方法。迭代器是指针)。
然后给它从vector<T>&
、vector<std::remove_const_t<T>> const&
、initializer_list<std::remove_const_t<T>>
等
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()});
}
根据 l
中的内容重新分配 unsigned
值的新缓冲区,并将其传递给 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>
.
虽然您无法在编译期间解压缩初始化列表(以执行必要的转换),但您可以按照您希望的方式创建它。考虑以下代码:
#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;
}