简体   繁体   English

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

[英]constexpr initializer_list raising an error: "expression must have a constant value -- reference or pointer to temporary with limited lifetime"

Here's the problem:这是问题所在:

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

I'm trying to compile the above with g++ (x86_64-posix-seh-rev0, version 8.1.0).我正在尝试使用 g++ (x86_64-posix-seh-rev0, version 8.1.0) 编译上述内容。 But VS Code raises the following warning:但是 VS Code 引发了以下警告:

"expression must have a constant value -- reference or pointer to temporary with limited lifetime" “表达式必须有一个常量值——生命周期有限的临时引用或指针”

When I remove the constexpr specifier, that is, when I amend the code to当我删除constexpr说明符时,也就是当我将代码修改为

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

the error goes away, so it seems there's something wrong with declaring a constexpr initializer_list .错误消失了,因此声明constexpr initializer_list似乎有问题。

Yet according to this reference ( https://en.cppreference.com/w/cpp/utility/initializer_list/initializer_list ), it should be perfectly fine to declare a constexpr initializer_list .然而,根据这个参考( https://en.cppreference.com/w/cpp/utility/initializer_list/initializer_list ),声明一个constexpr initializer_list应该是完全没问题的。

Can someone with deeper insight into the workings of constexpr and initializer_list explain why this is happening?constexprinitializer_list的工作有更深入了解的人可以解释为什么会发生这种情况吗?

There's a clue to the cause of this problem over at cppreference :cppreference有一个关于这个问题原因的线索:

An object of type std::initializer_list is a lightweight proxy object that provides access to an array of objects of type const T. std::initializer_list 类型的对象是一个轻量级代理对象,它提供对 const T 类型的对象数组的访问。

and:和:

Initializer lists may be implemented as a pair of pointers or pointer and length.初始化列表可以实现为一对指针或指针和长度。

In other words, when you do this at local scope:换句话说,当您在本地范围内执行此操作时:

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

the compiler has to allocate (and initialise) {1, 2, 3} on the stack, so it can't be constexpr .编译器必须在堆栈上分配(并初始化) {1, 2, 3} ,因此它不能是constexpr

Fortunately, there's an easy workaround.幸运的是,有一个简单的解决方法。 Just do:做就是了:

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

Now the underlying array is allocated at compile time and therefore can be constexpr .现在底层数组在编译时分配,因此可以是constexpr

The difference in the code generated can be seen at Godbolt .生成的代码的差异可以在Godbolt看到。


Edit: OP asks (in effect) can a stack-based variable ever be constexpr ?编辑: OP 询问(实际上)基于堆栈的变量可以是constexpr吗? Well, the answer is yes, if all the members of that object can be evaluated at compile time.好吧,答案是肯定的,如果该对象的所有成员都可以在编译时进行评估。

So, to go back to the original example, when you write:所以,回到原来的例子,当你写:

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

a std::initializer_list generally consists of a pointer to the underlying array and a length, so, here, the compiler has to push 1 , 2 and 3 onto the stack at runtime, and then put a pointer and a length to that "array" into my_ints , so my_ints cannot be constexpr since the address of said array is not known at compile time. std::initializer_list通常由指向底层数组的指针和长度组成,因此,在这里,编译器必须在运行时将123压入堆栈,然后将指针和长度放入该“数组” " 进入my_ints ,所以my_ints不能是constexpr因为在编译时不知道所述数组的地址。

OTOH, something like: OTOH,类似于:

constexpr int i = 42;

is fine, since 42 is (obviously) known at compile time and that's all the compiler cares about.很好,因为 42 在编译时(显然)是已知的,这就是编译器关心的全部。

Interestingly:有趣的是:

constexpr char s [] = "abcde";

also works.也有效。 If you look at the code generated , the compiler allocates "abcde" in static storage and then copies this to s at runtime.如果查看生成代码,编译器会在静态存储中分配“abcde”,然后在运行时将其复制到s Why it doesn't do something similar in the case of std::initializer_list is a bit of a mystery (to me, at least).为什么它在std::initializer_list的情况下不做类似的事情有点神秘(至少对我来说)。

And finally, why does declaring my_ints static fix things?最后,为什么声明my_ints static Well, now the compiler can allocate and initialise 1,2,3 at compile time and this "array" now is at an address which is known at compile time so my_ints can be constexpr .那么,现在的编译器可以分配并初始化1,2,3在编译时,该“阵列”现在处于其在编译时已知的,因此地址my_ints可以constexpr

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM