在Stack Overflow post Checking the object type in C++11 中,我有评论:
在C ++ 11中你实际上想做
virtual ~A() = default;
否则,你将失去隐含的移动构造函数。
什么是virtual ~A() = default;
?为什么隐式移动构造函数会丢失virtual ~A() {}
?
答案 0 :(得分:42)
评论不正确。
这两种:
virtual ~A() = default;
和
virtual ~A() {}
是用户声明。如果析构函数是用户声明的,则隐式移动成员将被禁止。
[dcl.fct.def.default] / p4讨论了用户声明的和用户提供的特殊成员:
如果是用户声明的,则特殊成员函数是用户提供的 未在第一次声明中明确默认或删除。
答案 1 :(得分:27)
在这篇文章https://stackoverflow.com/a/17204598/260127中,我有评论:
在C ++ 11中你实际上想做
virtual ~A() = default;
否则,你将失去隐含的移动构造函数。
评论不正确。
即使是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
复制
复制
移动
所以你没有“丢失”任何东西 - 开始时没有移动功能!
以下是禁止两个案例中隐式移动构造函数的标准段落:
时,才会隐式声明一个默认值。
[C++11: 12.8/9]:
如果类X
的定义没有明确声明一个移动构造函数,当且仅当
X
没有用户声明的复制构造函数,X
没有用户声明的副本分配运算符X
没有用户声明的移动分配运算符X
没有用户声明的析构函数,- 移动构造函数不会被隐式定义为已删除。
如果该标准的未来版本实际列出了诸如“用户声明”之类的术语的精确含义,则不会有任何损害。至少有这个:
[C++11: 8.4.2/4]:
[..] 特殊成员函数是用户提供的,如果它是用户声明的,并且在第一次声明时未明确默认或删除。 [..]
可以暗示这里的区别。
答案 2 :(得分:5)
该评论错误。
如果您希望编译器提供一个,而不是提供自己的移动构造函数,其中一个要求是它期望析构函数也由它提供,即一个简单的析构函数。但是,当前标准对于何时可以提供隐式实现非常严格 - 接受用户如何给出析构函数。用户声明的任何内容都被认为是用户自己掌握了这个问题,因此不仅仅是这个
~A() { … }
但也是这个
~A() = default;
使编译器不提供隐式析构函数。首先是定义,因此也是一个宣言;第二个是宣言。在这两种情况下,析构函数都是用户声明的,因此禁止编译器提供隐式移动构造函数。
我认为该要求背后的基本原理是,在 move 期间,对象的资源被移动到另一个对象,使原始对象处于动态存储中没有资源的状态;但是如果你的类没有任何这样的资源,那么它可以被轻易地移动,销毁等等。当你声明一个非平凡的析构函数时,它是编译器的一个提示,你在类中管理的资源不是一件小事,而是你大部分也必须提供非平凡的 move ,所以编译器不提供。