Re: Moving elements out of an initializer-list
On 16 Sep., 23:42, Johannes Schaub wrote:
SG wrote:
On 16 Sep., 22:24, Johannes Schaub wrote:
[...]
The draft guarantees that the initializer_list refers to that non-const
array, if "T" is nonconstant (by the text you quoted earlier from
8.5.4/4).
How can it guarantee that it refers to a non-const array when at the
same time it specifically allows a compiler to place the objects into
a ROM?
Well as I pointed out in my earlier response, it does not do so. All it has
is a note (!) that it may place it into a ROM when an explicit array may be
placed that way too. A non-const array cannot be placed into ROM so the
initializer_list won't do so either.
By that logic this optimization would only apply in cases where we
have a constructor like this:
struct X {
X(std::initializer_list<const double>);
};
X x {1,2,3};
(Note the "const double"). I don't believe this is the *intention* of
the draft. From what I can tell the wording is contradictory / worth
improving:
8.5.4/4:
"An object of type std::initializer_list<E> is constructed from
an initializer list as if the implementation allocated an array
of N elements 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. If a narrowing conversion is
required to initialize any of the elements, the program is
ill-formed. [ 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 ]"
8.5.4/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 ]
18.9/2:
"An object of type initializer_list<E> provides access to an
array of objects of type const E. [ Note: A pair of pointers or
a pointer plus a length would be obvious representations for
initializer_list. initializer_list is used to implement
initializer lists as specified in 8.5.4. Copying an initializer
list does not copy the underlying elements. --end note ]"
So, 8.5.4/4 links an initializer_list<E> object to an array of N
elements of type E. 18.9/2 links an initializer_list<E> object to an
array of N elements of type CONST E. Contradiction. Also, I firmly
believe that it is NOT intentional to only allow this ROM optimization
to be used in case E is itself const. I find it much more likely that
someone forgot to add a couple of "const"s in 8.5.4/4.
Cheers!
SG