为什么在定义析构函数时隐式删除移动构造函数

Why is the move constructor implicitly deleted when a destructor is defined

本文关键字:删除 移动 构造函数 定义 析构函数 为什么      更新时间:2023-10-16

我想知道为什么委员会决定在定义析构函数时隐式删除移动构造函数。

#include <iostream>
#include <vector>
#include <memory>
struct A { 
~A(){}; 
std::unique_ptr<int> a;
};

int main()
{
A a;
A b = std::move(a);
}

http://coliru.stacked-crooked.com/a/c0c067fc51260794

是否有任何乌托邦用例,这个规则"不默认移动成员"是有意义的?

逻辑是:如果您定义一个析构函数,暗示它在那里释放资源,那么编译器生成的构造函数和赋值可能是不够的。

这种想法是,如果您发现需要为类声明析构函数或复制特殊成员,则该类必须包含需要特殊处理的资源,因此隐式声明移动特殊成员可能很危险,因为生成的代码可能会导致不正确的行为。

一个简单的例子是

struct String
{
char *s = nullptr;
size_t size = 0;
String(char const* s); // makes a copy of the string
~String()
{
delete[] s;
}
};

如果标准允许隐式移动构造函数生成,它会做什么?它只会初始化目标对象中的ssize,但不会将它们分别分配给源对象中的nullptr0。然后,这会导致源对象和目标对象的析构函数和未定义行为中的双重删除。

复制赋值运算符的隐式生成也会导致类似的问题。


请注意,对于上面的示例,C++11 也弃用了复制特殊成员的隐式生成。不幸的是,它们不能定义为已删除,因为它会破坏太多代码。

[class.copy.ctor]/6

如果类定义未显式声明复制构造函数,则隐式声明非显式构造函数。如果类定义声明移动构造函数或移动赋值运算符,则隐式声明的复制构造函数定义为已删除;否则,它被定义为默认值。 如果类具有用户声明的复制赋值运算符或用户声明的析构函数,则不推荐使用后一种情况

相关文章: