initializer_list<T>不能转换为 initializer_list<U>,但 T 可转换为 U

initializer_list<T> can't convert to initializer_list<U>, but T convertable to U

本文关键字:initializer gt lt list 可转换 不能 转换      更新时间:2023-10-16

考虑以下代码片段...

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 来允许转换。是否可以以某种方式解开具有错误类型的列表并重新创建具有正确类型的新列表?

不。 构造初始值设定项列表需要编译时已知长度,而使用初始值设定项列表时没有编译时已知长度。

初始值设定项列表用于"面向用户"的操作,其中用户是接口的使用者。 像这样在内部使用它们效果不佳。

您可以使用的一种方法是编写一个 array_view<T> 类型,该类型以类似范围视图的方式(使用开始、结束、数据、大小、空、前端、后端方法(包装一系列连续内存(初始值设定项列表、向量、std 数组或 C 数组都是这方面的示例(。 迭代器是指针(。

然后给它隐式转换 ctor 从 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()});
}

go an 根据 l 中的任何内容重新分配unsigned值的新缓冲区,并将其传递给 booboo消耗一个array_view<unsigned const>,它可以从vectorunsignedinitializer_list转换。

然后我们可以写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;
}

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>这种可能性就会消失。

相关文章: