eye*_*ash 11 c++ language-lawyer stdinitializerlist
以下程序在 GCC 和 Clang 中编译时不会发出警告,并产生预期的输出:
#include <initializer_list>
#include <iostream>
constexpr std::initializer_list<std::initializer_list<const char*>> list = {
{"a", "b", "c"},
{"d"}
};
int main() {
for (const auto& outer: list) {
std::cout << "level:\n";
for (const auto& inner: outer) {
std::cout << " " << inner << "\n";
}
}
}
Run Code Online (Sandbox Code Playgroud)
然而,使用 MSVC,该程序根本不会产生任何输出。
根据 C++ 标准,该程序有效吗?这是 MSVC 中的错误吗?如果这不是有效的 C++ 那么为什么 GCC 或 Clang 没有发出警告?有没有更好的方法来创建constexpr内部列表没有固定大小的嵌套列表?
[dcl.init.list]/6(来自 C++20 草案 N4860)指出
该数组与任何其他临时对象 (6.7.7) 具有相同的生命周期,除了从数组初始化初始化器列表对象延长了数组的生命周期,就像将引用绑定到临时对象一样。
它们包括以下示例:
void f() {
std::vector<cmplx> v2{ 1, 2, 3 };
std::initializer_list<int> i3 = { 1, 2, 3 };
}
struct A {
std::initializer_list<int> i4;
A() : i4{ 1, 2, 3 } {} // ill-formed, would create a dangling reference
};
Run Code Online (Sandbox Code Playgroud)
该标准在同一段落中继续说道
对于 v1 和 v2,initializer_list 对象是函数调用中的参数,因此为 { 1, 2, 3 } 创建的数组具有完整表达式生命周期。对于 i3,initializer_list 对象是一个变量,因此该数组在变量的生命周期内持续存在。对于 i4,initializer_list 对象在构造函数的 ctor-initializer 中初始化,就像将临时数组绑定到引用成员一样,因此程序格式错误 (11.10.2)。
(强调我的)
我相信,在你的例子中,它相当于 example i3;因此初始化器的使用是有效的。然而,constexpr我认为,这才是导致问题的原因。如果删除constexpr,MSVC、g++ 和 clang 都会很高兴并执行代码。
我可能是错的,但我实际上认为这是 MSVC 中的一个错误。运行代码时,MSVC 以STATUS_ACCESS_VIOLATION. 我假设这是因为地址不再有效,它在打印时尝试引用 - 使用int而不是const char*一致地为我打印0,尽管我希望它更加随机,就像访问未初始化的内存一样。