c++ - 编译器对析构函数省略的自由度是多少?

标签 c++ c++11 destructor language-lawyer

众所周知,在某些情况下,编译器可能会忽略对复制构造函数的调用。但是,标准明确指出,编译器只能自由更改运行时行为(调用或不调用复制构造函数),但执行翻译时就像调用复制构造函数一样。特别是,编译器会检查是否有有效的复制构造函数可以调用。

我遇到了这样一种情况,即可能会省略析构函数调用,但编译器在是否需要存在有效析构函数方面存在差异。

这是一个完整的示例,展示了此问题如何发生以及编译器的行为有何不同。

template <typename T>
struct A {
  ~A() { (void) sizeof(T); }
};

struct B;    // defined elsewhere.

struct C {
  A<B> x, y;
  ~C();      // defined in a TU where B is complete.
};

int main() {
  C c;
}

编译时main()编译器生成 C的默认构造函数。此构造函数默认首先初始化 x然后 y .如果在 y 期间抛出异常 build ,然后x必须销毁。生成的代码如下所示:

new ((void*) &this->x) A<B>;   // default initializes this->x.
try {
  new ((void*) &this->y) A<B>; // default initializes this->y.
}
catch (...) {
  (this->x).~A<B>();           // destroys this->x.
  throw;
}

知道 A<B>的默认构造函数是微不足道的(并且不会抛出),在 as-if 规则下,编译器可能会将代码简化为:

new ((void*) &this->x) A<B>;   // default initializes this->x.
new ((void*) &this->y) A<B>;   // default initializes this->y.

因此,无需调用 ~A<B>() . (实际上,编译器甚至可以删除上面的两个初始化,因为 A<B> 的构造函数是微不足道的,但这对于本次讨论并不重要。)

问题是:即使对析构函数的调用可能被省略,编译器是否应该验证有效的析构函数是否可用?我在标准中找不到任何可以澄清问题的内容。谁能提供相关的报价?

如果编译器决定不翻译 ~A<B>() (就像 gcc 和 Visual Studio 一样)然后编译成功。

但是,如果编译器决定翻译 ~A<B>()无论如何(就像 clang 和 icc 一样),然后它会引发一个错误,因为这里 B是不完整的类型,无法取其大小。

最佳答案

我不认为这是标准规定的。如果 ~A<B>被实例化然后它是格式错误的并且需要诊断。正如你所说,如果构建 y抛出,然后 x必须销毁。

但是,构造 y永远不能抛出,因此可以说永远不会要求析构函数的定义存在(15.2/2、14.7.1/3)。

关于c++ - 编译器对析构函数省略的自由度是多少?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23184712/

相关文章:

c++ - std::string::erase 如何从字符串中删除字符?

c++ - 是否有一个总称来对 "from-scratch"构造函数进行分组,以将它们与复制和移动构造函数区分开来?

c++ - 尝试更改 ActivePowerScheme : RegOpenKeyEx failed with error 0

c++ - 无法编译 : unrecognized relocation

c++ - 如何将 g++ 和 gtest 集成到一个 cmake 文件中

c++ - 转换具有多个参数的构造函数

c++ - 当对象超出范围时是否调用析构函数?

c++ - 默认析构函数做了多少

php - PHP如何在脚本关闭时使用对象析构函数中的trigger_error触发用户错误?

c++ - 比较两个 end() 迭代器