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

我遇到了一种情况,其中可能会取消析构函数调用,但编译器在是否需要存在有效析构函数方面存在分歧。

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

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>的默认构造函数是微不足道的(并且不会抛出),根据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是不完整的类型,并且无法获取其大小。

===============>>#1 票数:1

我认为这不是标准规定的。 如果实例化〜A ~A<B> ,则其格式不正确,需要进行诊断。 正如您所说,如果构造y引发,则x必须被销毁。

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

  ask by Cassio Neri translate from so

未解决问题?本站智能推荐:

1回复

需要声明析构函数作为默认值

根据这些指导方针: 如果需要默认析构函数,但其​​生成已被抑制(例如,通过定义移动构造函数),请使用=default 。 我无法想象,如果没有具有移动构造函数的类中的显式默认析构函数,代码将会格式错误。 有人可以告诉我上面的例子确认报价吗?
1回复

c ++析构函数返回类型

析构函数是一个特殊的成员函数,它不带参数,也没有返回类型:几乎所有的c ++书籍都讲述了它。 但是,在libstd ++库中,它使用以下内容来测试类型是否可以破坏, Gnu g ++会显示带有typeid void的_U,所以析构函数会返回一个类型吗? 专家请解释一下c ++标准对此
3回复

如何使用非平凡的析构函数来防止未使用的变量警告

当我依赖生命周期扩展来分配具有非平凡析构函数的类时,编译器(gcc和clang)都会发出未使用的变量警告。 反正有没有绕过这个? https://wandbox.org/permlink/qURr4oliu90xJpqr 请注意,当我切换到不依赖于生命周期扩展时,事情工作得很好htt
2回复

具有默认受保护的析构函数的类不是微不足道的,但派生类是?

在以下示例中,第一个静态断言被触发,而第二个则不触发: (已与GCC,Clang,MSVC,ellcc核对) 我不明白为什么在B内部A不能被trivially_destructible破坏,而B却是微不足道的。 这似乎与C ++标准的以下两段矛盾,在这两段中未提及可访问性:
2回复

编译器是否真的强制执行纯虚析构函数?

为了验证语句“ 编译器和链接器强制存在的纯虚拟析构函数的函数体。 ”这篇geeksforgeeks文章 ,我编译了这段代码: 编译没有任何错误。 那么为什么编译器在这种情况下没有选择强制执行函数体的存在呢?
4回复

纯虚拟析构函数的目的是什么? [重复]

可能重复: 在什么情况下提供纯虚函数的实现是有利的? 为什么我们需要在C ++中使用纯虚拟析构函数? 当Base具有纯virtual析构函数时,编译器不强制Child class实现析构函数。 有趣的是那个; 编译器而不是强制Base定义析构函数体。 这是理解的
1回复

当提到析构函数时,标准中的“调用”是什么意思?

调用是否意味着析构函数已被调用并完成/返回,或者仅仅是被调用? 我一直以为是后者,直到我在12.4析构函数部分中看到了这句话(多个草稿都这样): 为对象调用析构函数后,该对象不再存在 如果被调用,则意味着调用已经开始,这意味着在析构函数的主体中this调用无效。 如果调
2回复

typedef别名的析构函数

上述程序的输出是: 我假设前两行属于用户析构函数调用,而第三行是由于在退出main函数范围时调用析构函数。 根据我的理解,typedef是类型的别名。 在这种情况下, AB是A的别名。 为什么这也适用于析构函数的名称? 非常感谢对语言规范的引用。 编辑:这是在ma
1回复

在尝试块的展开期间抛出析构函数会导致这种奇怪行为的原因是什么?

当try-block遇到异常时,堆栈被解开。 如果在try-block中创建了一个对象,则会调用析构函数。 如果析构函数抛出另一个异常,则不会捕获此异常并终止程序。 所以如果你有: 然后你的try-catch块是这样的: 然后当try-block完成时,抛出a2的析构函
2回复

函数调用中的隐式析构函数执行

我想知道标准对以下代码段怎么说。 可以在调用printPointer之前执行临时对象的string析构函数吗? ps VS2010编译器不会抱怨此代码,并且可以正常工作。