当替换完整对象时,基类子对象的“透明替换”?

"Transparent replacement" of baseclass subobject when complete object is replaced?

提问人:JMC 提问时间:8/30/2023 最后编辑:Jan SchultkeJMC 更新时间:8/30/2023 访问量:86

问:

请考虑以下代码片段:

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 ?

根据 [basic.life] p8,新的应该透明地取代 .但是,新的 -subobject 不应透明地替换,因为它是一个“可能重叠的子对象”,是一个基类子对象。BfooBAfoo.A

但是,更换附件是否足够有效?B

我有两种可能的解释:

[basic.life] p8

[...]原始对象的名称将自动引用新对象 [...]

因此,现在指的是新的.fooB

[expr.ref] p6.2 对 -member 访问表达式有如下说明:.

如果是非静态数据成员 [...],则表达式 [] 指定第一个表达式指定的对象的相应成员子对象。E2E1.E2

因此应该参考新的 n。foo.nfoo

另一种解释是,它本身作为一个整体,被视为[basic.life]的措辞(见上文)中的“原始对象的名称”,因此它不是指新的,因为它本身是不可透明的可替换的,因为它的包含对象,即,是不可透明的。foo.nintintA

如果有的话,这些解释中哪一种可能是正确的?


请记住,这是一个问题。问题不在于这是否实际有效,而在于标准的“法律条文”。

language-lawyer C++ placement-new

评论


答:

4赞 Jan Schultke 8/30/2023 #1

第一种解释是正确的。在示例中:

B foo;
new (&foo) B { {42} };

B foo可以透明地替换为在其存储中创建的类型的新对象,因为 [basic.life] p8 中的所有要求都已满足。 因此,对象的名称现在是指新对象,并且:Bfoo

int i = foo.n; // this is now valid, and foo.n refers to the subobject in
               // the new object also named foo

为什么第二种解释无效

foo.n不能被认为是一个名字;它是组合多个名称的表达式。我不认为“名称”是由标准正式定义的,但这个词与标识符密切相关,如[lex.name]所示。它在 [dcl.pre] p4 中的使用方式表明,最多可以是 、 或 的名称。xx::y...x::y

无论名称是什么,(即 )都不是对象,而是非静态数据成员。你也可以写,使这一点更明显,并且是等价的。[expr.ref] p6.2 定义了对象表达式和非静态数据成员。nA::nfoo.A::nE1.E2E1E2

结论

透明替换是有效的。 不是透明的可替换的,但这个没关系,因为你把替换作为一个整体,然后抬头看看里面的替换。A::nBA::nB

of 的子对象不必单独透明地替换即可实现。foo