std::initializer_list<int const> 不能从 std::initializer_list<int> 构造

std::initializer_list<int const> cannot be constructed from std::initializer_list<int>

我的问题是 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!

假定 语言规范要求默认为std::initializer_list<U> 推导非 cv 合格的 U

据我所知,具有 std::initializer_list 构造函数重载的 std::optional(以及 std::anystd::variant)的全部意义在于避免指定确切的类型初始化列表。要编译上述代码的第二行,这正是您必须做的。

std::initializer_list 已经拥有一个 const* 到它的数据(无论如何在 libc++ 中)。我没有理由看到上面的代码不起作用?这是语言中可以解决的问题,还是我遗漏了什么?

听起来你的问题是为什么 std::initializer_list<T const> 不能从 std::initializer_list<T> 构造,尽管实现这样的转换很容易。

我认为答案是您首先不应该拥有 std::initializer_list<T const>,因为正如您指出的那样,std::initializer_list<T> 只允许 const 访问其元素无论如何。

因此可以说 C++ 中有一个 "cultural norm",您不应该有任何需要 std::initializer_list<T const> 参数的构造函数。例如,标准库容器的 None 会这样做,因为 cv 限定的值类型无论如何都是非法的(除了 std::array 的情况,当然,它无论如何都没有用户定义的构造函数) .

如果你自己写的类型MyContainer支持MyContainer<T const>,那我建议你做成下面这样:

template <class T>
class MyContainer {
  public:
    MyContainer(std::initializer_list<std::remove_cv_t<T>> il);
    // ...
};