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

pro*_*eek 49 c++ destructor virtual-functions c++11

在Stack Overflow post中检查C++ 11中的对象类型,我有以下注释:

在C++ 11中你实际上想要做virtual ~A() = default;其他事情,你将失去隐含的移动构造函数.

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

How*_*ant 42

评论不正确.

都:

virtual ~A() = default;
Run Code Online (Sandbox Code Playgroud)

virtual ~A() {}
Run Code Online (Sandbox Code Playgroud)

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

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

如果特殊成员函数是用户声明的,并且在其第一个声明中未明确默认或删除,则由用户提供.


Lig*_*ica 27

在这篇文章/sf/answers/1204321891/,我有评论:

在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
    } 
}
Run Code Online (Sandbox Code Playgroud)

现场演示:

+ g ++ - 4.8 -std = c ++ 11 -O2 -Wall -pthread main.cpp
+ ./a.out
copy
copy
move

所以你没有"丢失"任何东西 - 开始时没有移动功能!

以下是在两种情况下禁止隐式移动构造函数的标准段落:

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

  • X 没有用户声明的复制构造函数,
  • X 没有用户声明的复制赋值运算符,
  • X 没有用户声明的移动赋值运算符,
  • X没有用户声明的析构函数,和
  • 移动构造函数不会被隐式定义为已删除.

Bootnote

如果该标准的未来版本实际列出了诸如"用户声明"之类的术语的精确含义,则不会受到伤害.至少有这样的:

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

人们可以通过暗示来区分这里.


leg*_*s2k 5

这个评论是错误的.

如果您希望编译器提供一个,而不是提供您自己的移动构造函数,其中一个要求是它期望析构函数也由它提供,即一个简单的析构函数.但是,当前标准对于何时可以提供隐式实现非常严格 - 接受用户如何给出析构函数.用户声明的任何内容都被认为是用户自己掌握了这个问题而不仅仅是这个问题

~A() { … }
Run Code Online (Sandbox Code Playgroud)

还有这个

~A() = default;
Run Code Online (Sandbox Code Playgroud)

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

我想这个要求背后的基本原理是,在移动过程中,对象的资源被移动到另一个对象,使原始对象处于动态存储中没有资源的状态; 但是如果你的类没有任何这样的资源,那么它可以被轻易地移动,销毁等等.当你声明一个非平凡的析构函数时,它是编译器的一个提示,你在类中管理的资源不是一件小事,而是你大部分也必须提供非平凡的动作,所以编译器不提供一个.

  • +1用于评论此行为背后的原因. (4认同)