为什么我们需要使用virtual〜A()= default; 而不是虚拟〜A(){}在C + + 11?

在堆栈溢出后检查C ++ 11中的对象types ,我有评论:

在C ++ 11中,你实际上想要做virtual ~A() = default; 否则,你将失去implict构造函数。

什么是virtual ~A() = default; 对于? 如何隐式地移动构造函数与virtual ~A() {}

评论是不正确的。

都:

 virtual ~A() = default; 

 virtual ~A() {} 

是用户声明的 。 如果析构函数是用户声明的,则隐式移动成员将被禁止。

[dcl.fct.def.default] / p4讨论用户声明用户提供的特殊成员:

一个特殊的成员函数是用户提供的,如果它是用户声明的,并且没有显式默认或删除它的第一个声明。

在这篇文章https://stackoverflow.com/a/17204598/260127 ,我有评论:

在C ++ 11中,你实际上想要做virtual ~A() = default; 否则,你将失去implict构造函数。

评论是不正确的。

即使default编辑,该析构函数是“ 用户声明 ”(虽然注意,它也不是“ 用户提供 ”)。

 #include <iostream> struct Helper { Helper() {} Helper(const Helper& src) { std::cout << "copy\n"; } Helper(Helper&& src) { std::cout << "move\n"; } }; struct A { virtual ~A() {} Helper h; }; struct B { virtual ~B() = default; Helper h; }; struct C { Helper h; }; int main() { { A x; A y(std::move(x)); // outputs "copy", because no move possible } { B x; B y(std::move(x)); // outputs "copy", because still no move possible } { C x; C y(std::move(x)); // outputs "move", because no user-declared dtor } } 

现场演示 :

+ g ++ – 4.8 -std = c ++ 11 -O2 -Wall -pthread main.cpp
+ ./a.out
复制
复制
移动

所以你没有“丢失”任何东西 – 那里没有任何移动function!

下面是在这两种情况下禁止隐式移动构造函数的标准通道:

[C++11: 12.8/9]:如果一个类X的定义没有明确声明一个移动构造函数,当且仅当它被隐式声明为默认

  • X没有用户声明的拷贝构造函数,
  • X没有用户声明的复制赋值操作符,
  • X没有用户声明的移动赋值操作符,
  • X没有用户声明的析构函数 ,并且
  • 移动构造函数不会被隐式定义为删除。

Bootnote

如果标准的未来版本实际上列出了诸如“用户声明”之类的术语的确切含义,则不会受到伤害。 至less有这样的:

[C++11: 8.4.2/4]: [..]一个特殊的成员函数是用户提供的,如果它是用户声明的,并且没有明确地默认或删除它的第一个声明。 [..]

人们可以通过暗示假设这个区别。

那个评论是错误的。

如果你想让编译器提供一个,那么它的一个要求就是它期望析构函数也是由它提供的,也就是一个简单的析构函数,而不是提供你自己的移动构造函数。 然而,当前的标准在可以提供隐式实现的时候非常严格 – 接受用户给出析构函数的方式。 用户声明的任何东西都被认为是用户正在把事情掌握在自己手中,因此不仅如此

 ~A() { … } 

也是这个

 ~A() = default; 

使编译器不提供隐式的析构函数。 首先是一个定义,也就是一个声明。 第二只是一个声明。 在这两种情况下,析构函数都是用户声明的,因此禁止编译器提供隐式移动构造函数。

我想这个要求背后的基本原理是,在移动过程中,一个对象的资源被移动到另一个对象,使原来的对象处于没有dynamic存储资源的状态。 但如果你的类没有任何这样的资源,那么它可以被平凡地移动,销毁等等。当你声明一个不平凡的析构函数时,它是编译器的一个提示,你在类中pipe理的资源不是微不足道的你大多不得不提供非平凡的举动 ,所以编译器不提供一个。