使用MSVC编译器时,constexpr初始化列表没有产生输出

11

以下代码在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";
        }
    }
}

然而,使用MSVC编译器,该程序没有输出任何内容。

根据C++标准,这个程序是有效的吗?这是MSVC的一个错误吗?如果这不是有效的C++代码,为什么GCC或Clang没有发出警告?是否有更好的方法来创建具有可变大小内部列表的constexpr嵌套列表?


2
当运行时,MSVC会返回STATUS_ACCESS_VIOLATION。我可能错了,但我认为这是未定义行为。由于初始化列表在运行时被循环遍历,编译时的地址如何仍然有效?(猜测) - ChrisMM
1
编译器差异 - 实时 - https://godbolt.org/z/WG5Mevnox - +1 - Richard Critten
1
官方称初始化列表仅为临时对象(底层数组是类型为const T[N]的临时数组)。 https://en.cppreference.com/w/cpp/utility/initializer_list 据我所知,初始化列表只能在临时上下文中安全使用(例如传递给构造函数)。免责声明:我没有查看标准的确切措辞。 - Pepijn Kramer
@PepijnKramer 你说的部分正确,在你提供的链接中还有这样一句话:底层数组的生命周期与任何其他临时对象相同,除了从数组初始化initializer_list对象会像绑定到临时对象的引用一样延长数组的生命周期(具有相同的例外情况,例如初始化非静态类成员)。 - NathanOliver
不知道当前状态如何,但似乎initializer_lists有一段历史 ;) https://dev59.com/M2Qo5IYBdhLWcg3wbe5K, https://dev59.com/RV4c5IYBdhLWcg3wz9FG. 无论如何,阅读所有这些内容都没有改变我的心理模型,即我不能将std::initializer列表用作变量(无论是constexpr还是非constexpr)。如果有人真正知道的话,那就太好了 :) - Pepijn Kramer
1个回答

0

[dcl.init.list]/6(来自C++20草案N4860)规定:

该数组与任何其他临时对象一样具有相同的生命周期(6.7.7),但是使用该数组初始化initializer_list对象会像将引用绑定到临时对象一样扩展该数组的生命周期。

以下是示例:

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
};

标准在同一段落中继续说明:

对于v1和v2,initializer_list对象是函数调用中的参数,因此为{1, 2, 3}创建的数组具有完整表达式生命周期。对于i3,initializer_list对象是一个变量,因此数组在变量的生命周期内持久存在。对于i4,initializer_list对象在构造函数的ctor-initializer中初始化,就像将临时数组绑定到引用成员一样,因此程序是不合法的(11.10.2)。

(强调是我的)

在您的示例中,我认为它等同于示例i3;因此,使用initializer是有效的。但是,我认为constexpr是导致问题的原因。如果删除constexpr,MSVC、g++和clang都可以正常执行代码。

我可能错了,但我认为这实际上是MSVC中的一个错误。运行代码时,MSVC会退出并显示STATUS_ACCESS_VIOLATION。我猜测这是因为在打印时它正在尝试引用的地址不再有效--使用int而不是const char*一致为我打印0,尽管我希望它更像访问未初始化的内存那样随机。

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