我可以在构造函数的主体中转发构造吗?

Can I forward construction in the body of a constructor?

本文关键字:转发 构造函数 主体 我可以      更新时间:2023-10-16

让我们考虑一下,在执行类S的构造函数时,似乎可以使用另一个构造函数构造S。一种解决方案可能是在this上新建一个放置位置以重用存储:

struct S{
unsigned int j; //no const neither reference non static members
S(unsigned int i){/*...*/}
S(int i){
if (i>=0) {
new (this) S(static_cast<unsigned int>(i));
return;}
/*...*/
}
};
int i=10;
S x{i};//is it UB?

存储重用在 [basic.life] 中定义。我不知道在构造函数执行期间(重新(使用存储时如何阅读本节。

在这种情况下,标准完全未指定,我找不到相关的 CWG 问题。

就其本身而言,您的新展示位置不是 UB。毕竟,您拥有没有对象的存储,因此您可以直接在其中构造一个对象。正如您正确所说,第一个对象的生存期尚未开始。

但现在的问题是:原始对象会发生什么变化?因为通常情况下,构造函数只在没有对象的存储上调用,构造函数的结束标志着对象的生存期的开始。但是现在已经有另一个对象了。新对象是否被销毁?它没有效果吗?

该标准在[class.cdtor]中缺少一个段落,该段落说明了如果在正在构建和销毁的对象存储中创建新对象时会发生什么。

你甚至可以构建更奇怪的代码:

struct X {
X *object;
int var;
X() : object(new (this) X(4)), var(5) {} // ?!?
X(int x) : var(x) {}
} x;

是 UB 吗?

不,不是。[basic.life]/5说:

程序可以通过重用对象占用的存储或使用非平凡析构函数显式调用类类型的对象的析构函数来结束任何对象的生存期。对于具有非平凡析构函数的类类型的对象,程序不需要在重用或释放对象占用的存储之前显式调用析构函数;但是,如果没有显式调用析构函数,或者未使用 delete-expression 来释放存储,则不应隐式调用析构函数,并且依赖于析构函数产生的副作用的任何程序都具有未定义的行为。

强调与您的类相关的部分,该部分具有微不足道的析构函数。关于具体的new (this) T;形式,我在[class.cdtor][class.dtor]中都没有发现这条规则的例外。