constexprinitializer_list 使用 MSVC 不产生输出

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内部列表没有固定大小的嵌套列表?

Chr*_*sMM 0

[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,尽管我希望它更加随机,就像访问未初始化的内存一样。