【问题标题】:unrestricted union members lifetime during parent object construction父对象构造期间不受限制的联合成员生命周期
【发布时间】:2017-09-19 19:10:39
【问题描述】:

通常,您对不受限制的工会成员的生命周期负责 - 通常您通过就地 ctor/dtor 调用来做到这一点。但是,显然,至少在一种情况下编译器可以帮助你——在下面的代码中,如果对象构造失败,它(以前构造的)联合成员会自动销毁(至少在 MSVC 2015 中),即我们永远不会泄漏。

#include <string>

struct CanThrow
{
    CanThrow() {  throw 0;  }
};

struct A
{
    A() : str{} {}    // note that we don't explicitly call str dtor here
    ~A() { str.~basic_string(); }

    union { std::string str; };
    CanThrow ct;
};

int main() { try{ A a; } catch(...) {} }

免责声明:此代码在我的 MSVC 2015 上编译

问题——这是由标准保证的吗?它在哪里规定?

【问题讨论】:

  • 你怎么知道它被破坏了?
  • 析构函数中的断点

标签: c++ c++11 unions


【解决方案1】:

恰恰相反:它应该发生!

[C++11: 9.5/8]: 类似联合的类是联合或具有匿名联合作为直接成员的类。类似联合的类X 有一组变体成员。如果X 是联合,则其变体成员是非静态数据成员; 否则,它的变体成员是所有匿名联合的非静态数据成员,它们是X 的成员。

[C++11: 15.2/2]: 任何存储持续时间的对象,如果其初始化或销毁被异常终止,将为其所有完全构造的子对象(不包括联合类的变体成员)执行析构函数,即对于主要构造函数 (12.6.2) 已完成执行且析构函数尚未开始执行的子对象。类似地,如果一个对象的非委托构造函数已经完成执行,并且该对象的委托构造函数异常退出,则将调用该对象的析构函数。如果对象是在 new 表达式中分配的,则调用匹配的释放函数(3.7.4.2、5.3.4、12.5)(如果有)以释放对象占用的存储空间。

如果 Visual Studio 这样做,那么它是不合规的; FWIW,GCC 6.3 seems to be also.

请注意,(当前)C++17 的措辞是不同的,并且确实允许我们观察到的内容; this change appears to have been introduced by CWG issue #1866 in 2014,因此很可能这是主要编译器“领先于游戏”并且不太遵守标准的时代之一(尽管有 -std 标志)。

所以答案是,,该标准当然不能保证在 MSVS 2015 的情况下,但它会在未来 C++17 版本的软件。

【讨论】:

  • 这肯定发生在 MSVC2015 中。使用 ~basic_string() 中的断点检查调试器
  • GCC 也被破坏了——在 main() 中的构造周围添加 try/catch。您的示例失败,因为转义异常不会展开堆栈。很抱歉——我应该添加它。
  • @C.M.:嗯,好点哈哈,那么是的,除非我误读了什么,否则两者似乎都坏了。
  • 看起来整个功能完全是broken...为什么我在语言的最基本构建块中总是遇到类似的问题?
  • 顺便说一句,编译器正在尝试做正确的事情——在这种情况下不调用 dtor 是一种语言缺陷......如果我的班级中有三个这样的联合并尝试在ctor 初始值设定项列表——我无法确定在异常情况下需要销毁哪个。
猜你喜欢
  • 2020-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-15
相关资源
最近更新 更多