Please consider the following code snippet:
struct A { int n; };
struct B : A {};
B foo;
new (&foo) B { {42} }; // the new B transparently-replaces foo
int i = foo.n; // is this valid? Does this correctly refer to the new B's A.n ?
According to [basic.life] p8, the new B
should transparently replace foo
. However, the new B
's A
-subobject should not transparently replace foo.A
because it is a "potentially-overlapping subobject", being a base class subobject.
However, is it enough for the enclosing B
to be replaced for this to be valid?
I have two possible interpretations:
[basic.life] p8 says that
[...] the name of the original object will automatically refer to the new object [...]
thus, foo
now refers to the new B
.
[expr.ref] p6.2 says the following about the .
-member access expression:
If
E2
is a non-static data member [...] the expression [E1.E2
] designates the corresponding member subobject of the object designated by the first expression.
Therefore foo.n
should refer to the new foo
's n.
The other interpretation is that foo.n
is itself, as a whole, considered the "name of the original object" as in the wording (see above) of [basic.life], and it therefore does not refer to the new int
because that int
itself is not transparently-replaceable, because its encompassing object, the A
, is not transparently-replaceable.
Which of these interpretations might be correct, if any?
Please keep in mind that this is a language-lawyer question. The question is not so much about whether this practically works, but about the "letter of the law" of the standard.
The first interpretation is correct. In the example:
B foo;
new (&foo) B { {42} };
B foo
is transparently replaceable by the new object of type B
created in its storage, since all requirements in [basic.life] p8 are met.
Therefore, the name of the object foo
now refers to the new object, and:
int i = foo.n; // this is now valid, and foo.n refers to the subobject in
// the new object also named foo
foo.n
can't be considered a name; it is an expression combining multiple names. I don't believe "name" is formally defined by the standard, but the word is strongly associated with identifier, as can be seen in [lex.name]. The way it's used in [dcl.pre] p4 suggests that at most x
, x::y
, or ...x::y
could be considred names.
Regardless of what a name is, n
(i.e. A::n
) is not an object, it is a non-static data member. You can also write foo.A::n
which makes this more obvious, and is equivalent. [expr.ref] p6.2 defines E1.E2
for an object expression E1
and a non-static data member E2
.
The transparent replacement is valid. A::n
is not transparently replaceable, but this is okay, because you replace B
as whole, and then look up A::n
within the replaced B
.
The subobjects of of foo
don't have to be individually transparently-replaceable for this to be possible.