简体   繁体   中英

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). But VS Code raises the following warning:

"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

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 .

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 .

Can someone with deeper insight into the workings of constexpr and initializer_list explain why this is happening?

There's a clue to the cause of this problem over at 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.

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 .

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 .

The difference in the code generated can be seen at Godbolt .


Edit: OP asks (in effect) can a stack-based variable ever be 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.

OTOH, something like:

constexpr int i = 42;

is fine, since 42 is (obviously) known at compile time and that's all the compiler cares about.

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. Why it doesn't do something similar in the case of std::initializer_list is a bit of a mystery (to me, at least).

And finally, why does declaring my_ints static fix things? 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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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