All your examples have well defined behavior. From §8.5.4/5
An object of type
std::initializer_list<E>
is constructed from an initializer list as if the implementation allocated a temporary array ofN
elements of typeconst E
, whereN
is the number of elements in the initializer list. Each element of that array is copy-initialized with the corresponding element of the initializer list, and thestd::initializer_list<E>
object is constructed to refer to that array. ... [ Example:
struct X {
X(std::initializer_list<double> v);
};
X x{ 1,2,3 };
The initialization will be implemented in a way roughly equivalent to this:
const double __a[3] = {double{1}, double{2}, double{3}};
X x(std::initializer_list<double>(__a, __a+3));
...—end example ]
Also, §8.5.4/6 is relevant
The array has the same lifetime as any other temporary object (12.2), except that initializing an initializer_list object from the array extends the lifetime of the array exactly like binding a reference to a temporary.
The standard even gives examples of valid and invalid code after that. The invalid code sample is this:
struct A {
std::initializer_list<int> i4;
A() : i4{ 1, 2, 3 } {} // creates an A with a dangling reference
};
It's invalid because the lifetime of the temporary array created for the initializer list ends when the constructor body ends, leaving i4
with dangling references.