I was playing with some useless code to understand initialization of member references, and bumped into this:
struct A {};
struct B
{
B() : a()
{
}
const A& a;
};
The code above gives the following error when compiled with gcc 4.9.2:
In constructor 'B::B()':
error: value-initialization of reference type 'const A&'
B() : a()
Which I understand.
But if I use uniform initialization in B's constructor's initializer list, like so:
struct A {};
struct B
{
B() : a{}
{
}
const A& a;
};
It compiles fine.
So the question is, why does the use of uniform initialization here change the compilation result?
I also tried this with Microsoft Visual C++ 2013.
It does not compile either version of the code, with the same error message:
Error 3 error C2440: 'initializing' : cannot convert from 'int' to 'const A &
You can have a quick play with it here:
http://ideone.com/7f2t8I
GCC is correct in its interpretation of {}. [dcl.init.list]/p3.8-9 (quoting N4296; earlier drafts has the same relative ordering of these two bullets):
List-initialization of an object or reference of type T is defined as
follows:
[7 inapplicable bullets omitted]
Otherwise, if T is a reference type, a prvalue temporary of the type referenced by T is copy-list-initialized or
direct-list-initialized, depending on the kind of initialization for
the reference, 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
]
Otherwise, if the initializer list has no elements, the object is value-initialized.
List-initializing the reference hits bullet 3.8, causing the construction of a temporary. The value-initialization case, in 3.9, doesn't apply.
Value-initialization of a reference is ill-formed ([dcl.init]/p9):
A program that calls for default-initialization or
value-initialization of an entity of reference type is ill-formed.
However, as of N4296, per [class.base.init]/p8:
A temporary expression bound to a reference member in a
mem-initializer is ill-formed.
This was added as a result of CWG issue 1696, which is a DR (defect report) against C++14.
Pre-CWG1696, the standard provided that (N4140 [class.temporary]/p5.1):
A temporary bound to a reference member in a constructor’s
ctor-initializer (12.6.2) persists until the constructor exits.
which means that the reference will become dangling immediately after construction. This presumably motivated CWG1696's decision to disallow such bindings altogether.
Related
This is a follow-up of these questions.
Consider the following code:
struct A {
private:
A* const& this_ref{this};
};
int main() {
A a{};
(void)a;
}
If compiled with the -Wextra, both GCC v6.2 and clang v3.9 show a warning.
Anyway, with the slightly modified version shown below they behave differently:
struct A {
A* const& this_ref{this};
};
int main() {
A a{};
(void)a;
}
In this case GCC doesn't give any warning, clang gives the same warning as returned in the previous example.
The warnings are almost the identical.
It follows the one from clang:
3 : warning: binding reference member 'this_ref' to a temporary value [-Wdangling-field]
Which compiler is right?
I would say that GCC is wrong in this case and I were opening an issue, but maybe it's the opposite because of an arcane corner case of the language.
The member declaration
A* const& this_ref{this};
binds a reference to a temporary that only exists during constructor execution (note: this is an rvalue expression).
I'm not sure if this is formally available in that context, but if it is then with any use of that pointer you have serious case of UB.
Re
” Which compiler is right?
… a compiler can issue as many diagnostics as it wants. It's not wrong to issue a diagnostic. So per your description, that both accept the code, then either both compilers are right (which I think is most likely), or both are wrong.
The reason for this warning is IMO this excerpt from standard (12.2.5):
A temporary bound to a reference member in a constructor’s ctor-initializer (12.6.2) persists until the
constructor exits.
and since the keyword this is a prvalue expression, during this_ref initialization a temporary will be created and this_ref is bound to that temporary.
But I have doubt whether your reference is actually initialized in ctor-initializer.
If you write:
struct A {
private:
const int& rr = 1+1;
};
then you will reproduce the exact same problem with gcc, removing private will also remove this warning.
From what I know this pointer might be used in the body of the non-static member function, I have never read that it could be used as argument during default member initialization.
this is prvalue, and temporary object will be created when binding reference to a prvalue, so you're binding reference member to a temporary in default member initializer.
And binding reference member to temporary in default member initializer is ill-formed, which is stated by the standard explicitly.
$12.6.2/11 Initializing bases and members
[class.base.init]:
A temporary expression bound to a reference member from a default
member initializer is ill-formed. [ Example:
struct A {
A() = default; // OK
A(int v) : v(v) { } // OK
const int& v = 42; // OK
};
A a1; // error: ill-formed binding of temporary to reference
A a2(1); // OK, unfortunately
— end example ]
And see CWG 1696, this is applied to C++14.
For the following code:
struct A {
explicit A(int) {}
};
const A& a(1); // error on g++/clang++/vc++
const A& b = 1; // error on g++/clang++/vc++
const A& c{1}; // ok on g++/clang++, error on vc++
const A& d = {1}; // error on g++/clang++/vc++
Which one(s) of the 4 initialization is(are) legal?
If we ignore vc++ first, it seems that the difference between direct-init and copy-init is not behaving consistently here. If the third one is well-formed because it's direct-init, why does the first one which is also direct-init fail to compile? What's the logic behind this? Or it's just a bug for g++/clang++ and vc++ handles it correctly?
If you're using braced-init-lists and the destination type of an initialization is a reference:
[dcl.init.list]/3 (from n3690)
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 copy-list-initialized or
direct-list-initialized, depending on the kind of initialization for
the reference, 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]
Otherwise, if the initializer list has no elements, the object is value-initialized.
For the two examples const A& c{1}; and const A& d = {1};, the second bullet of the quotation above applies. The first one direct-list-initializes a const A, the second one copy-list-initializes a const A. Copy-initialization selecting an explicit constructor is ill-formed, see [over.match.list]/1.
If you're not using braced-init-lists, there's no difference between copy-initialization and direct-initialization as far as I can tell. The last bullet of [dcl.init.ref]/5 applies in all cases:
Otherwise, a temporary of type “cv1 T1” is created and initialized from the initializer expression
using the rules for a non-reference copy-initialization (8.5). The reference is then bound to the
temporary.
Copy-initialization cannot select an explicit ctor, see [over.match.copy]/1 (it's not viable).
Conclusion:
const A& c{1};
is legal. The other are not, because they either use copy-initialization or copy-list-initialization and the only viable / selected ctor is explicit.
Your struct can only be created by an explicit call to it's constructor. No implicit conversion is allowed.
A const reference must point to an existing object of that type on construction.
None of your lines create an object of type A by calling it's explicit constructor. So I don't see why any of those lines should properly initialize a const reference.
C++
I'm trying to see how const references prolong the lifetime of temporaries. I'm running the code from the snippet in one of the answers to What are the differences between pointer variable and reference variable in C++? and got conflicting results between VC11 and g++ 4.8. I've expanded the snippet here:
#include <stdio.h>
struct scope_test
{
~scope_test() { printf("scope_test done!\n"); }
};
int main()
{
const scope_test& test = scope_test();
printf("in scope\n");
}
The answerer got the result:
in scope
scope_test done!
I tried it in VC11 and got this:
scope_test done!
in scope
scope_test done!
I assumed the VC11 result was caused by a lack of copy elision, so I tried to see if disabling copy elision on g++ with fno-elide-constructors would give the same result as VC11. (I don't think copy elision can be toggled in VC11.) But, g++ gives the answerer's result regardless of the setting of the flag.
The C++11 Standard, ISO/IEC 14882:2011(E), §12.2/4 and /5 states:
There are two contexts in which temporaries are destroyed at a
different point than the end of the full-expression...
The second context is when a reference is bound to a temporary. The
temporary to which the reference is bound or the temporary that is the
complete object of a subobject to which the reference is bound
persists for the lifetime of the reference except:
...
Does the VC11 result have anything to do with copy elision? Is it a VC11 bug?
The answerer states:
temporaries assigned to const references live until the const
reference goes out of scope
The list of exceptions to §12.2/5 don't rule out a non-const reference. What am I missing from the Standard?
Removing the const in VC11 produces the same result as the VC11 one with the const. Removing the const in g++ gives error: invalid initialization of non-const reference of type ‘scope_test&’ from an rvalue of type ‘scope_test’. Why is there a difference?
EDIT:
I added copy and move constructors and tried:
#include <stdio.h>
struct scope_test
{
scope_test() { printf("regular ctor\n"); }
scope_test(const scope_test& src) { printf("copy ctor\n"); }
scope_test(scope_test&& src) { printf("move ctor\n"); }
~scope_test() { printf("scope_test done!\n"); }
};
int main()
{
const scope_test& test= scope_test();
printf("in scope\n");
}
Regardless of the toggling of copy elision, g++ gives:
regular ctor
in scope
scope_test done!
VC11 gives the same thing, even if the const is removed. If the const is removed from g++, g++ still gives error: invalid initialization of non-const reference of type ‘scope_test&’ from an rvalue of type ‘scope_test’.
Both behaviors are correct, certainly according to the C++03 standard (8.5.3 [dcl.init.ref] paragraph 5):
Otherwise, the reference shall be to a non-volatile const type (i.e., cv1 shall be const). [Example: ...]
If the initializer expression is an rvalue, with T2 a class type, and “cv1 T1” is reference-compatible with “cv2 T2,” the reference is bound in one of the following ways (the choice is implementation-defined):
— The reference is bound to the object represented by the rvalue (see 3.10) or to a sub-object within that object.
— A temporary of type “cv1 T2” [sic] is created, and a constructor is called to copy the entire rvalue object into the temporary. The reference is bound to the temporary or to a sub-object within the temporary.
I think the definition of C++11 still allows the copy to be made but the wording doesn't as clearly allow the copy. In any case, VC++ doesn't claim to be fully C++11 compliant.
Consider the following code:
class A {
A(const A&);
public:
A() {}
};
int main() {
const A &a = A();
}
This code compiles fine with GCC 4.7.2, but fails to compile with Visual C++ 2010 with the following error:
test.cc(8) : error C2248: 'A::A' : cannot access private member declared in class 'A'
test.cc(2) : see declaration of 'A::A'
test.cc(1) : see declaration of 'A'
So is it necessary to have a copy constructor accessible when binding a temporary to a reference?
This is somewhat related to my previous question:
Is there a way to disable binding a temporary to a const reference?
So is it necessary to have a copy constructor accessible when binding a temporary to a reference?
Post C++11 - No
Pre C++11 - Yes.
This code compiles fine with GCC 4.7.2 because it is compliant with the C++11 standard.
C++11 standard mandates that when a const reference is initialized from prvalue, it must be bound directly to the reference object and no temporary is permitted to be created. Also, the copy constructor is not used or required.
Prior to C++11 the rules were different. And this behavior(whether copy constructor will be called) is implementation defined. C++03 allowed the copy constructor being called while binding a const reference to an temporary and hence post C++11 the copy constructor needs to be accessible. Visual C++2010 adheres to the C++03 standard.
Section 8.5.3.5 of the C++03 standard states that this is implementation-defined:
If the initializer expression is an rvalue, with T2 a class type, and "cv1 T1" is reference-compatible with "cv2 T2," the reference is bound in one of the following ways (the choice is implementation-defined):
-- The reference is bound to the object represented by the rvalue (see 3.10) or to a sub-object within that object.
-- A temporary of type "cv1 T2" [sic] is created, and a constructor is called to copy the entire rvalue object into the temporary. The reference is bound to the temporary or to a sub-object within the temporary.
The constructor that would be used to make the copy shall be callable whether or not the copy is actually done.
So it appears that both implementations are consistent with the C++03 standard.
The last sentence is a little confusing, but the way I read it, it means that the implementation may choose the second way, but still optimize away the copy. In that case, the copy constructor would have to be accessible even though the copy wasn't actually done, similar to return value optimization.
With the C++11 standard, the second way is no longer an option.
Visual C++ is incorrect; the Standard does not indicate that the copy constructor must be accessible to bind a const reference to a temporary.
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 };