关于这段代码:
#include <string>
int main()
{
union u {
u() { i = 0; }
~u() {}
int i;
std::string s1;
std::string s2;
} u;
new (&u) std::string{};
}
对象可以包含其他对象,称为子对象。子对象可以是成员子对象([class.mem])、基类子对象([class.derived])或数组元素。不是任何其他对象的子对象的对象称为完整对象。如果在与成员子对象或数组元素e相关联的存储中创建对象(可能在其生命周期内,也可能不在),则如果:
— e所包含的对象的生命周期已经开始而且没有结束,并且
— 新对象的存储正好覆盖与e相关联的存储位置,并且
— 新对象与e的类型相同(忽略cv限定符)。
则创建的对象是e所包含对象的子对象。
并没有要求如何在与成员子对象相关联的存储中创建对象。 如果子对象是标准布局联合的成员或非联合类对象的第一个成员,则代码不必在取地址运算符的参数中提名子对象。 在这种情况下,只需获取包含对象的地址即可指定成员子对象的存储。
“没有要求对象创建的方式”,这意味着提供给就地new的指针不必指向子对象。主要是因为可能没有对象可以指向(注意,[intro.object]/2不要求子对象存活)。在std-discussion邮件列表中,有人问,给定类型为struct A { unsigned char buf[1]; };
的对象x
,new (&x) A{}
和new (x.buf) A{}
之间是否有区别?答案是没有区别,在两种情况下,x.buf
都将提供存储空间给A{}
。因为
[intro.object]和[basic.life]中的措辞关注表示指针的存储地址,而不是指针所指向的对象。
[class.union]/1宣称:“联合类型的对象的非静态数据成员最多只能有一个处于活动状态。”
在上面的代码中,哪个成员变量变为了活动状态,是s1
还是s2
?
std::string
类型的联合成员的生命周期。 - Language Lawyer