Is the behavior of the following code well-defined?
struct X { int i; }; // trivial
struct Y : X { Y(){} }; // non-trivial
extern X xobj;
int& r1 = xobj.i; // #1
X xobj;
extern Y yobj;
Y& r2 = yobj; // #2
// int& r3 = yobj.i; // #3 - this is UB according to the standard
Y yobj;
This code is inspired by the example in the C++ standard, namely draft N4140 [class.cdtor]/1.
That's what that paragraph reads:
For an object with a non-trivial constructor, referring to any non-static member or base class of the object before the constructor begins execution results in undefined behavior. For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor finishes execution results in undefined behavior.
An example follows, which shows how pointers may and may not be bound to objects.
So intuitively it seems that #1
and #2
are well-defined, while #3
invokes UB if uncommented, but, first, examples are not normative, second, there's no mention of references in the example, and third and the most important, the above paragraph doesn't imply that otherwise the behavior is well-defined. Or does it? Or maybe there's another relevant quote in the standard that I missed?
Edit: The answer may (arguably) be yes if the objects have static storage duration, but they can be also local, e.g:
struct A { A(){} };
struct B { B(A&){} };
struct C {
B b;
A a;
C() : b(a) {}
};
int main() {
C c;
}
Actually this was the initial inspiration for this question, see Circular dependency in constructor initialization list
The behavior of #2 is definitely well-defined. As mentioned by @dyp, the relevant paragraph is in [basic.life]:
Binding the glvalue yobj
to a reference is fine, since its storage lasts throughout the duration of the program ([basic.stc.static]/1) and the reference is bound to a valid object - aliveness aside - which meets the requirement in ([dcl.ref]/5). Similarly, for the second example you showed, as long as no operation is performed on members of the A
subobject, the above paragraph applies as well since the constructor of C
is called on the allocated storage this
refers to.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With