Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is expected lifetime of std::intializer_list object in C++14?

Please consider this simplified c++14 program:

#include <iostream>

struct A
{
    A() { std::cout << "A() "; }
    ~A() { std::cout << "~A() "; }
};

int main()
{
    auto l = std::initializer_list<A>{A()};
    std::cout << ". ";
}

https://gcc.godbolt.org/z/1GWvGfxne

GCC prints here

A() . ~A()

Meaning that std::initializer_list is destructed at the end of scope.

Clang prints:

A() ~A() . 

Destroying std::initializer_list in the line where it is constructed.

Are both compiler behave correctly here or one of them is wrong?

like image 471
Fedor Avatar asked Jul 16 '21 11:07

Fedor


Video Answer


1 Answers

It's subtle.

A std::initializer_list is backed by an underlying array (produced by the compiler). This array is a like a temporary object, and the std::initializer_list is a sort of reference type that binds to it. So it will extend the temporary array's lifetime so long as the "reference" exist.

In C++14, we do not have guaranteed copy elision. So what should happen is as though std::initializer_list<A>{A()} produced a temporary initializer_list, bound another temporary array to it, and copied the temporary initializer_list to l.

std::initializer_list behaves like a regular reference, as far as lifetime extension is concerned. Only the original reference extends the lifetime, and our original is temporary itself. So the underlying array goes out of existence at the end of the full expression containing the initialization of l. Clang is the correct one.

Direct-initialization ...

std::initializer_list<A> l {A()};

... produces the same output on both compilers.

Meanwhile, your original code behaves the same on GCC and Clang when compiling for C++17.

like image 117
StoryTeller - Unslander Monica Avatar answered Oct 20 '22 04:10

StoryTeller - Unslander Monica