无法使用std::initializer_list<int>构造std::initializer_list<int const>。

3
我的问题是关于在 std::initializer_list 类型之间缺少转换的问题,其中包含的类型具有更多或更少的cv限定符,尽管这些转换似乎很容易实现。考虑以下无效代码:
std::initializer_list<int> x{1,2,3,4,5,6};
std::initializer_list<int const> y = x; // error! cannot convert!

现在考虑一下std::optional,忽略荒谬的类型:
std::optional<std::vector<int>> x{std::in_place, {1,2,3,4,5}}; // OK
std::optional<std::vector<int const>> y{std::in_place, {1,2,3,4,5}}; // error!

我假设语言规范要求默认推导非cv限定的std::initializer_list<U>中的U类型。

据我所知,std::optional(以及std::anystd::variant)具有std::initializer_list构造函数重载的整个目的是避免指定初始化列表的确切类型。为了使上面代码的第二行编译通过,这正是您必须做的。

std::initializer_list已经在其数据中持有const*(在libc++中)。我看不到上述代码无法工作的理由?这是一种可解决的语言问题,还是我漏掉了什么?


3
在 C++ 中,vector<int const> 明确是不合法的,因此很难“忽略荒谬的类型”。那么你为什么期望 initializer_list<T const> 能够正常工作呢?你会用它来做什么初始化? - Nicol Bolas
老实说,我不知道。当时我在写一个通用的包装类型时发现了这个问题。 - xcvr
也许创建一个类似于dynarray的类型会更加合适。 - xcvr
根据这个线程,我认为vector<int const>意外地不合法的,它可能会在C++17中变得合法,但并非出于意图,而是作为其他更改的副产品。 - M.M
虽然有趣,但我的问题的重点与std::vector无关。 - xcvr
显示剩余2条评论
1个回答

4
听起来你的问题是为什么尽管很容易实现这样的转换,但std :: initializer_list <T const>不能从std :: initializer_list <T>构造。
我认为答案是,首先你不应该有std :: initializer_list <T const>,因为正如你所指出的,std :: initializer_list <T>只提供对其元素的const访问。
因此,可以说在C ++中存在一种“文化规范”,即你不应该有任何需要std :: initializer_list <T const>参数的构造函数。例如,标准库容器都没有,因为cv限定值类型是非法的(除了std :: array之外,当然,它根本没有用户定义的构造函数)。
如果你编写自己的类型MyContainer来支持MyContainer <T const>,那么建议它看起来像以下内容:
template <class T>
class MyContainer {
  public:
    MyContainer(std::initializer_list<std::remove_cv_t<T>> il);
    // ...
};

“文化规范”似乎是正确的!我会把这个问题留着,以防有人(你?)能够解释为什么所有初始化列表如果具有与“std::initializer_list<T>”相同的语义,为什么它们不能被推导为“std::initializer_list<const T>”。 - xcvr
1
由于std::initializer_list的模板参数是使用模板参数推导的通常规则推导出来的,因此它永远不会推导出带有cv限定符的类型、引用类型、数组类型或函数类型。 - Brian Bi
尽管我认为当类型无法推导除非它被指定为std::initializer_list<cv U>时,模板参数推导的通常规则已经不再适用。 - xcvr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接