【问题标题】:Can I forward construction in the body of a constructor?我可以在构造函数的主体中转发构造吗?
【发布时间】:2019-02-25 15:03:05
【问题描述】:

让我们考虑在执行类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] 中定义。在构造函数执行期间(重新)使用存储时,我不知道如何阅读本节。

【问题讨论】:

  • 使用委托构造函数和/或工厂?
  • @Jarod42 问题是这些都是无条件的。 OP 正试图根据运行时参数值仅委托某些时间。
  • @Angew S(int i) : S(MakeS(i)) {} 带有移动构造函数(或完全省略:-))。
  • 构造函数将是错误的地方(在运行时完成时),因此您必须求助于类似工厂的方法
  • if (i&gt;=0) j = static_cast&lt;unsigned int&gt;(i); else j = .... 也许?

标签: c++ constructor language-lawyer


【解决方案1】:

在这种情况下,标准完全没有明确规定,我找不到相关的 CWG 问题。

就其本身而言,您的新展示位置不是 UB。毕竟你有没有对象的存储,所以你可以直接在里面构造一个对象。正如你所说的,第一个对象的生命周期还没有开始。

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

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

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

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

【讨论】:

    【解决方案2】:

    是 UB 吗?

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

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

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

    【讨论】:

    • 另外,请注意,该类不必有一个微不足道的析构函数(你说“强调”在析构函数上,但你似乎强调了另一部分?)
    • @Rakete1111 我的意思是,“或者通过显式调用具有非平凡析构函数的类类型对象的析构函数。” 与 OP 无关,因为 @987654325 @ 有一个微不足道的析构函数。
    • 我的问题是结束生命周期,因为根据[basic.life]/1.2 类型T的对象的生命周期开始于:如果对象有非空初始化,其初始化完成。所以我们不能结束生命,因为它还没有开始。
    猜你喜欢
    • 1970-01-01
    • 2011-11-05
    • 2011-04-21
    • 2010-09-23
    • 1970-01-01
    • 1970-01-01
    • 2010-12-19
    • 2014-09-02
    相关资源
    最近更新 更多