C++0x, exact semantics of initializer_list<E>

S

SG

Hi!

In a Google tech talk, Bjarne Stroustrup answered an audience question
about whether it is possible to return std::initializer_list objects
from functions with "Yes.". However, after having read the relevant
sections of the recent draft (N3090.pdf)...

section 8.5.4 ("List initialization"):

4 An object of type std::initializer_list<E> is constructed from an
initializer list as if the implementation allocated an array of N
elelemts of type E, where N 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 the
std::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:

double __a[3] = {double{1}, double{2}, double{3}};
X x(std::initializer_list<double>(__a, __a+3));

assuming that the implementation can construct an initializer_list
object with a pair of pointers. -end example ]

5 The lifetime of the array is the same as that of the
initializer_list object. [ Example:

typedef std::complex<double> cmplx;
std::vector<cmplx> v1 = { 1, 2, 3 };
void f() {
std::vector<cmplx> v2{ 1, 2, 3 };
std::initializer_list<int> i3 = { 1, 2, 3 };
}

For v1 and v2, the initializer_list object and array created for
{ 1, 2, 3 } have full-expression lifetime. For i3, the
initializer_list object and array have automatic lifetime.
-end example ] [ Note: The implementation is free to allocate the
array in read-only memory if an explicit array with the same
initializer could be so allocated. —end note ]

section 18.9 ("Initializer lists")

2 [...] Copying an initializer list does not copy the underlying
elements.

....I have trouble figuring out whether Stroustrup was right or wrong.
The standard doesn't require the compiler to store the array
statically initialized in a read-only section. So, in the worst case
the array has automatic storage and the array will be destroyed before
the function returns (see implementation example from 8.5.4/4).

std::initializer_list<double> foo() {
return {1.0, 3.0};
}

equivalent to

std::initializer_list<double> foo() {
double __ila[2] = {1.0,3.0};
return std::initializer_list<double>(ila,ila+2);
}

?

But this would be a contradiction to 8.5.4/5 because the life-time of
the std::initializer_list surpasses the life-time of the array in this
case -- at least when copy elision (RVO) is applied.

So, my question is:
Is returning an initializer_list from a function actually supposed to
work reliably?
I'm guessing the answer is "no".


Cheers!
SG
 
A

Alf P. Steinbach /Usenet

* SG, on 22.07.2010 15:23:
Hi!

In a Google tech talk, Bjarne Stroustrup answered an audience question
about whether it is possible to return std::initializer_list objects
from functions with "Yes.". However, after having read the relevant
sections of the recent draft (N3090.pdf)...

section 8.5.4 ("List initialization"):

4 An object of type std::initializer_list<E> is constructed from an
initializer list as if the implementation allocated an array of N
elelemts of type E, where N 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 the
std::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:

double __a[3] = {double{1}, double{2}, double{3}};
X x(std::initializer_list<double>(__a, __a+3));

assuming that the implementation can construct an initializer_list
object with a pair of pointers. -end example ]

5 The lifetime of the array is the same as that of the
initializer_list object. [ Example:

typedef std::complex<double> cmplx;
std::vector<cmplx> v1 = { 1, 2, 3 };
void f() {
std::vector<cmplx> v2{ 1, 2, 3 };
std::initializer_list<int> i3 = { 1, 2, 3 };
}

For v1 and v2, the initializer_list object and array created for
{ 1, 2, 3 } have full-expression lifetime. For i3, the
initializer_list object and array have automatic lifetime.
-end example ] [ Note: The implementation is free to allocate the
array in read-only memory if an explicit array with the same
initializer could be so allocated. —end note ]

section 18.9 ("Initializer lists")

2 [...] Copying an initializer list does not copy the underlying
elements.

...I have trouble figuring out whether Stroustrup was right or wrong.
The standard doesn't require the compiler to store the array
statically initialized in a read-only section. So, in the worst case
the array has automatic storage and the array will be destroyed before
the function returns (see implementation example from 8.5.4/4).

std::initializer_list<double> foo() {
return {1.0, 3.0};
}

equivalent to

std::initializer_list<double> foo() {
double __ila[2] = {1.0,3.0};
return std::initializer_list<double>(ila,ila+2);
}

?

But this would be a contradiction to 8.5.4/5 because the life-time of
the std::initializer_list surpasses the life-time of the array in this
case -- at least when copy elision (RVO) is applied.

So, my question is:
Is returning an initializer_list from a function actually supposed to
work reliably?
I'm guessing the answer is "no".

I'm guessing the answer is the same as for returning a pointer or reference.

After all an initializer_list seems to be no more than two pointers.

E.g. you might return an initializer list constructed from a static array, or
one that was passed into the function.


Cheers & hth.,

- Alf
 
S

SG

I'm guessing the answer is the same as for returning a pointer
or reference.

After all an initializer_list seems to be no more than two
pointers.

Judjung by the examples and comments, yes. And I guess that anything
more complicated (array on stack OR heap with ref-counting) is not
what the designers had in mind.
E.g. you might return an initializer list constructed from
a static array

As far as I can tell initializer_list objects are only created via the
list initialization syntax. In cases like {1,2,3} it should be easy
for the compiler to create a static array (much like string literals)
but this behaviour is not guaranteed, so, it doesn't seem to work
reliably.

Cheers,
SG
 
A

Alf P. Steinbach /Usenet

* SG, on 22.07.2010 19:14:
Judjung by the examples and comments, yes. And I guess that anything
more complicated (array on stack OR heap with ref-counting) is not
what the designers had in mind.


As far as I can tell initializer_list objects are only created via the
list initialization syntax. In cases like {1,2,3} it should be easy
for the compiler to create a static array (much like string literals)
but this behaviour is not guaranteed, so, it doesn't seem to work
reliably.

Well, I read up on it.

Initialization of an object O with initializer_list argument constructor, from a
brace initializer, works /as if/ (my words) an array A is created with same
lifetime as the initializer_list formal constructor argument, that array A is
initialized from the brace initializer, and the initializer_list object is set
to point to begin and end of that array A.

Since initializer_list objects are copyable initializer_list has a copy
constructor, hence an initializer_list object can be initialized from a brace
initializer, hence, what about just a 'static std::initializer_list<T> O = ..."?

However, then we're into non-normative example territory.

For formally the initializer_list object that governs the lifetime of the array
would be the copy constructor's object, which doesn't exist very long, so the
array A would only have full-expression lifetime. However, the non-normative
example in §8.5.4/5 makes the claim that when the object O that is initialized
by a brace expression is itself of type initializer_list, then the array has the
same lifetime as O (and not just as its copy constructor formal argument).

Anyway, this seems to work with g++ 4.4.1, while MSVC 10.0 chokes on its own
<initializer_list> header.


Cheers,

- Alf
 
D

Dilip

Anyway, this seems to work with g++ 4.4.1, while MSVC 10.0 chokes on its own
<initializer_list> header.

Alf

Take a look at this link:
http://channel9.msdn.com/shows/Goin...Template-Library-STL-2-of-n/?CommentID=562782

That post includes a comment from Stephen T Lavavej (owner of STL at
Microsoft) at the very end. He says:
btw, will the initializer list be implemented in visual studio soon ?
Magic 8 Ball says: "Ask again later."
(I really want initializer lists, but they're actually not in my top
three list of desired features. Also, please ignore the
<initializer_list> header that I accidentally left in VC10 RTM. It
doesn't do anything.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,480
Members
44,900
Latest member
Nell636132

Latest Threads

Top