Reputation: 25526
Coming from this question, I am going a step further:
C* c = static_cast<C*>(malloc(sizeof(C)));
As stated in the referenced question, accessing *c (its members) is undefined behaviour before a constructor is called. But, the pointer itself is valid, of course.
Now within the constructor, the members are available already and I should be able to take the address off.
Putting this together, I conclude that the following is legal:
class Y;
class X
{
Y& y;
public:
X(Y& y) : y(y) { } // non-trivial constructor!
};
class Y
{
X& x;
public:
Y(X& x) : x(x) { }
};
class Z
{
X x;
Y y;
public:
Z() : x(y), y(x) { }
};
as long as the constructor of X does not use the uninitialized reference to Y other than storing it somewhere.
Is this correct or have I overseen some important point (and thus producing UB again)? If I missed something, would it make a difference if I used pointers instead of references?
Upvotes: 4
Views: 402
Reputation: 49986
It should be fine, but you are not allowed to use y
inside of X(Y& y)
as it was not yet initialized.
The relevant part that says it is not UB is:
3.7.5/6 n4140 (emphasis is mine)
Similarly, before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any glvalue that refers to the original object may be used but only in limited ways.
y
is lvalue (which is also a glvalue), so above is relevant here. Accessing variables using such reference is UB.
Standard also says that reference to be valid it must (8.3.2/5):
... A reference shall be initialized to refer to a valid object or function.
But I have not found in standard what is valid object
. Especially whether it means that its constructor has already been called. Using pointer instead of reference seems to not have this problem.
Upvotes: 2
Reputation: 10316
This is legitimate. To justify this, see the following from the standard, [basic.life]:
- Before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that refers to the storage location where the object will be or was located may be used but only in limited ways. For an object under construction or destruction, see 12.7. Otherwise, such a pointer refers to allocated storage (3.7.4.2), and using the pointer as if the pointer were of type void*, is well-defined. ...
...
- Similarly, before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any glvalue that refers to the original object may be used but only in limited ways. For an object under construction or destruction, see 12.7. Otherwise, such a glvalue refers to allocated storage (3.7.4.2), and using the properties of the glvalue that do not depend on its value is well-defined.
...
Merely taking a reference falls under the 'limited use' (for objects whose lifetime has not yet begun) criteria set out above.
Upvotes: 2