什么's编译器's析构函数省略的自由
What's the compiler's freedom for destructor elision?
众所周知,在某些条件下,编译器可能会取消对复制构造函数的调用。然而,该标准明确指出,编译器只有更改运行时行为的自由(调用或不调用复制构造函数),但执行翻译时就像调用复制构造函数一样。特别是,编译器检查是否有一个有效的复制构造函数可以调用。
我遇到了这样一种情况:析构函数调用可能被忽略,但编译器在是否需要存在有效的析构函数方面存在差异。
下面是一个完整的例子,展示了这个问题是如何发生的,以及编译器的行为有何不同。
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
构造期间引发异常,则必须销毁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>
的默认构造函数是琐碎的(并且不会抛出),在假设规则下,编译器可能会将代码简化为:
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++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 内联映射初始化的动态atexit析构函数崩溃
- 什么时候调用析构函数
- 优先顺序:智能指针和类析构函数
- C++-明确何时以及如何调用析构函数
- 使用基类指针创建对象时,缺少派生类析构函数
- 在c++中使用向量时,如何调用构造函数和析构函数
- 重载运算符new[]的行为取决于析构函数
- 我需要知道编译器如何在cpp中使用析构函数
- 为什么在使用转换构造函数赋值后调用C++类的析构函数?
- 析构函数调用
- 通过引用传递-为什么要调用这个析构函数
- 对具有动态分配的内存和析构函数的类对象的引用
- 重载 -> shared_ptr 个实例中的箭头运算符<interface>,接口中没有纯虚拟析构函数
- C++成员的析构函数顺序与shared_ptr
- 为什么标准允许我在没有析构函数的情况下自由存储分配类
- 删除和自由在C++中有什么区别,C++的默认析构函数调用哪一个?
- 错误:双重自由或损坏(fasttop) -由析构函数引起,但如何
- 什么's编译器's析构函数省略的自由