constexpr initializer_list引发错误:“表达式必须具有恒定值-寿命有限的临时引用或指针”

问题描述

问题出在这里

int main() 
{
    constexpr std::initializer_list<int> my_ints {1,2,3};
}

我正在尝试使用g ++(x86_64-posix-seh-rev0,版本8.1.0)编译以上代码。但是VS Code提出以下警告:

“表达式必须具有恒定值-寿命有限的临时引用或指针”

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

int main() 
{
    std::initializer_list<int> my_ints {1,3};
}

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

根据此参考文献(https://en.cppreference.com/w/cpp/utility/initializer_list/initializer_list),最好声明一个constexpr initializer_list

可以更深入地了解constexprinitializer_list的工作原理的人吗?

解决方法

cppreference处有一个导致此问题的原因的线索:

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

和:

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

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

std::initializer_list<int> my_ints {1,2,3};

编译器必须在堆栈上分配(并初始化){1,3},因此不能为constexpr

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

static constexpr std::initializer_list<int> my_ints {1,3};

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

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


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

因此,回到原始示例,当您编写:

std::initializer_list<int> my_ints {1,3};

std::initializer_list通常由指向基础数组的指针和长度组成,因此,这里的编译器必须将123推入在运行时堆栈,然后将指向该“数组”的指针和长度放入my_ints中,因此my_ints不能为constexpr,因为在编译时未知该数组的地址。

OTOH,类似:

constexpr int i = 42;

很好,因为(显然)在编译时就知道42,这是编译器关心的所有事情。

有趣的是:

constexpr char s [] = "abcde";

也可以。如果您查看code generated,则编译器会在静态存储中分配“ abcde”,然后在运行时将其复制到s。为什么在std::initializer_list上它没有做类似的事情(至少对我来说是个谜)。

最后,为什么声明my_ints static可以解决问题?好了,现在编译器可以在编译时分配1,3并将其初始化,并且现在 的“数组”位于编译时已知的地址,因此my_ints可以是{{ 1}}。