constexpr initializer_list 引发错误:“表达式必须具有常量值——引用或指向具有有限生命周期的临时指针”

Ask*_*ker 1 c++ intellisense initializer-list constexpr visual-studio-code

这是问题所在:

int main() 
{
    constexpr std::initializer_list<int> my_ints {1, 2, 3};
}
Run Code Online (Sandbox Code Playgroud)

我正在尝试使用 g++ (x86_64-posix-seh-rev0, version 8.1.0) 编译上述内容。但是 VS Code 引发了以下警告:

“表达式必须有一个常量值——生命周期有限的临时引用或指针”

当我删除说明constexpr符时,也就是当我将代码修改为

int main() 
{
    std::initializer_list<int> my_ints {1, 2, 3};
}
Run Code Online (Sandbox Code Playgroud)

错误消失了,所以声明 a 似乎有问题constexpr initializer_list

然而根据这个参考(https://en.cppreference.com/w/cpp/utility/initializer_list/initializer_list),声明一个constexpr initializer_list.

有人可以更深入地了解其工作原理constexprinitializer_list解释为什么会发生这种情况吗?

Pau*_*ers 5

cppreference有一个关于这个问题原因的线索:

std::initializer_list 类型的对象是一个轻量级代理对象,它提供对 const T 类型的对象数组的访问。

和:

初始化列表可以实现为一对指针或指针和长度。

换句话说,当您在本地范围内执行此操作时:

std::initializer_list<int> my_ints {1, 2, 3};
Run Code Online (Sandbox Code Playgroud)

编译器必须{1, 2, 3}在堆栈上分配(和初始化),所以它不能是constexpr.

幸运的是,有一个简单的解决方法。做就是了:

static constexpr std::initializer_list<int> my_ints {1, 2, 3};
Run Code Online (Sandbox Code Playgroud)

现在底层数组是在编译时分配的,因此可以是constexpr.

生成的代码的差异可以在Godbolt看到。


编辑: OP 询问(实际上)可以基于堆栈的变量constexpr吗?好吧,答案是肯定的,如果该对象的所有成员都可以在编译时进行评估。

所以,回到原来的例子,当你写:

std::initializer_list<int> my_ints {1, 2, 3};
Run Code Online (Sandbox Code Playgroud)

astd::initializer_list通常由一个指向底层数组的指针和一个长度组成,因此,在这里,编译器必须在运行时将1,23推入堆栈,然后将指向该“数组”的指针和长度放入my_ints,因此my_ints不能constexpr因为所述数组的地址在编译时未知。

OTOH,类似于:

constexpr int i = 42;
Run Code Online (Sandbox Code Playgroud)

很好,因为 42 在编译时(显然)是已知的,这就是编译器关心的全部。

有趣的是:

constexpr char s [] = "abcde";
Run Code Online (Sandbox Code Playgroud)

也有效。如果您查看生成代码,编译器会在静态存储中分配“abcde”,然后s在运行时将其复制到。为什么它不做类似的事情std::initializer_list有点神秘(至少对我来说)。

最后,为什么声明可以my_ints static解决问题?那么,现在的编译器可以分配并初始化1,2,3在编译时,该“阵列”现在在其在编译时已知这样的地址my_intsconstexpr