为什么拥有析构函数会使 class 不可复制?

Why does having a destructor make a class uncopyable?

我有一个无法编译的简单程序

#include <future>

class Foo
{
public:
    ~Foo();
    std::future<int> f;
};

Foo getFoo()
{
    return Foo();
}

int main()
{
    Foo b = getFoo();
}

我想这很有道理。 Foo 不可复制,因为 future 不可复制。

 In function 'Foo getFoo()':
13:16: error: use of deleted function 'Foo::Foo(const Foo&)'
4:7: note: 'Foo::Foo(const Foo&)' is implicitly deleted because the default definition would be ill-formed:
4:7: error: use of deleted function 'std::future<_Res>::future(const std::future<_Res>&) [with _Res = int]'
In file included from 2:0:
/usr/include/c++/4.9/future:686:7: note: declared here
       future(const future&) = delete;
       ^
 In function 'int main()':
18:20: error: use of deleted function 'Foo::Foo(const Foo&)'

我不明白的是为什么当我删除析构函数时它会编译:

#include <future>

class Foo
{
public:
    //~Foo();
    std::future<int> f;
};

Foo getFoo()
{
    return Foo();
}

int main()
{
    Foo b = getFoo();
}

反正这个析构函数不是默认生成的吗?它对 class 成为 copyable/movable 有什么影响?

In c++11, The implicit definition of a copy constructor as defaulted is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor.

这是语言的标准。

请注意,您也有一个不可复制的成员。

隐式生成的复制构造函数被定义为已删除,因为您的class包含一个std::future,它是不可复制的。这意味着您的 class 在任何一种情况下都不可复制。

然而它可能是可移动的。通常您显示的示例也可以使用 移动构造函数 ,但是当您手动声明析构函数时未声明隐式移动构造函数,使得第一个示例中的 Foo 也不可移动,而它在第二个。

正如@Ayxan 在问题下的评论中提到的那样,由于 C++17,您的两个示例都可以编译,因为尽管 Foo 在第一个示例中不可移动,因为 C++17 是强制性的复制省略规则适用,并且在您的示例中只会将一个 Foo 直接构建到 Foo b 中。不会存在临时 Foo 对象,因此 class 不需要可复制或移动。


一般来说,如果您有自定义析构函数,您应该始终手动实现 copy/move 构造函数和赋值运算符。即所谓的rule of 3/5。该语言当前不对复制操作强制执行此规则,尽管自 C++11 以来,如果存在用户声明的析构函数,则复制操作的隐式声明已被弃用。但是,如果存在用户声明的析构函数,该语言会通过不隐式声明移动操作来强制执行移动操作规则。

自定义析构函数并不经常需要,因此您可能一开始就不要声明它。如果您需要声明它,因为例如你需要它是 virtual(在这种情况下它应该默认为 virtual ~Foo() = default),然后你可以默认移动操作:

Foo(Foo&&) = default;
Foo& operator=(Foo&&) = default;

但是,如果您的析构函数有一个自定义定义,其中确实包含实际执行某些工作的语句,您可能必须使用适当的语义自己实现这些方法,这样您就不会 运行 在以下情况下遇到问题class 已移动。