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 值的新缓冲区,并将其传递给 booboo 消耗一个 array_view<unsigned const>,它可以从 vectorinitializer_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;
}