49

在 Stack Overflow post Checking the object type in C++11中,我有评论:

在 C++11 中,您实际上会想要这样做virtual ~A() = default;,否则,您将失去隐式移动构造函数。

virtual ~A() = default;为了什么?隐式移动构造函数怎么会丢失virtual ~A() {}

4

3 回答 3

42

评论不正确。

两个都:

virtual ~A() = default;

virtual ~A() {}

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

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

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

于 2013-06-20T19:26:19.367 回答
27

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

在 C++11 中,您实际上会想要这样做virtual ~A() = default;,否则,您将失去隐式移动构造函数。

评论不正确。

甚至defaulted,该析构函数是“用户声明的”(但请注意,它也不是“用户提供的”)。

#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]: [..]一个特殊的成员函数是用户提供的,如果它是用户声明的并且在其第一次声明时没有显式地默认或删除。[..]

可以通过暗示假设这里的区别。

于 2013-06-20T19:33:17.557 回答
5

那个评论是错误的。

如果您希望编译器提供一个,而不是提供您自己的移动构造函数,则要求之一是它期望析构函数也由它提供,即一个微不足道的析构函数。然而,当前的标准对何时可以提供隐式实现非常严格——接受用户如何给出析构函数。用户声明的任何内容都被认为是用户将此事掌握在自己手中,因此不仅如此

~A() { … }

但这也是

~A() = default;

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

我想这个要求背后的基本原理是,在移动过程中,一个对象的资源被移动到另一个对象,使原始对象处于动态存储中没有资源的状态;但是如果你的类没有任何这样的资源,那么它可以被简单地移动、销毁等。当你声明一个非平凡的析构函数时,它会提示编译器你在类中管理的资源不是微不足道的,而且您通常也必须提供非平凡的举动,因此编译器不提供。

于 2013-06-20T19:05:46.240 回答