That is, why does this:
struct S {};
struct T
{
T(S& s) : s{s} {}
S& s;
};
int main()
{
S s;
T t{s};
}
give me a compiler error with GCC 4.7:
test.cpp: In constructor 'T::T(S&)':
test.cpp:5:18: error: invalid initialization of non-const reference of type 'S&' from an rvalue of type '<brace-enclosed initializer list>'
?
To fix the error, I have to change the s{s}
to s(s)
. Doesn't this break the, erm, uniformity of uniform initialization?
EDIT: I tried with clang, and clang accepts it, so perhaps it's a GCC bug?
Yes, its a bug. This is something new and was voted in the working paper in February 2012 (link).
Nicol Bolas makes a good point in that gcc is actually the conforming compiler according to the FDIS approved C++11 standard because the changes to the working paper were made after that.
I believe that to be an error in the compiler. The two paragraphs that deal with reference initialization through list-initialization are (in n3337):
§8.5.4/3
List-initialization of an object or reference of type T is defined as follows:
Otherwise, if the initializer list has a single element of type E and either T is not a reference type or its referenced type is reference-related to E, the object or reference is initialized from that element; if a narrowing conversion (see below) is required to convert the element to T, the program is ill-formed.
Otherwise, if T is a reference type, a prvalue temporary of the type referenced by T is list-initialized, and the reference is bound to that temporary. [ Note: As usual, the binding will fail and the program is ill-formed if the reference type is an lvalue reference to a non-const type. — end note ]
The compiler seems to be applying the last paragraph, when it should be applying the first, as reference-related is defined as
8.5.3/4
Given types “cv1 T1” and “cv2 T2,” “cv1 T1” is reference-related to “cv2 T2” if T1 is the same type as T2, or T1 is a base class of T2.
In the case of the question, the types of the reference and the initializer inside the brace-initialization-list are exactly the same, which means that the initialization should be valid.
In the FDIS draft, the equivalent paragraphs had the order reversed. The implication is that the FDIS draft (n3290) did not allow for brace-list-initialization of *lvalue*s. On the other hand, reading the text it seems obvious that it is a bug in the standard and that the intention was having the order of n3337:
Otherwise, if T is a reference type, a prvalue temporary of the type referenced by T is list-initialized, and the reference is bound to that temporary.
Otherwise, if the initializer list has a single element, the object or reference is initialized from that element; if a narrowing conversion (see below) is required to convert the element to T, the program is ill-formed.
The order in that document means that because all reference types are handled by the first clause, mentioning reference in the following paragraph would make no sense.
(Note: I'm writing this answer with the benefit of 2 years of hindsight since the original question; and to put some of the information from comments into an actual answer so that it is searchable).
Of course, initializing a reference of type S&
with a reference also of type S&
is supposed to bind directly.
The problem is a defect in the C++11 standard and was addressed by DR1288. The corrected text appears in C++14.
The Committee has clarified that the corrected text is what was intended for C++11, and so a "conforming compiler" should implement the corrected version.
g++ 4.8 followed the published text of the C++11 standard; however once this issue came to light, g++ 4.9 implemented the corrected version, even with -std=c++11
switch.
Note that the problem is not confined to constructor initializer lists either, e.g.: S s; S &t{s};
doesn't work in g++ 4.8, nor does S s; S &t = s; S &u { t };
来源:https://stackoverflow.com/questions/10509603/why-cant-i-initialize-a-reference-in-an-initializer-list-with-uniform-initializ