C++ C++;11虚拟析构函数和移动特殊功能的自动生成

C++ C++;11虚拟析构函数和移动特殊功能的自动生成,c++,c++11,inheritance,move-semantics,C++,C++11,Inheritance,Move Semantics,C++11中自动生成特殊移动函数(构造函数和赋值运算符)的规则规定不能声明析构函数。其逻辑大概是,如果你需要在销毁中做一些特殊的事情,那么移动可能不安全 但是,为了在多态性中正确调用析构函数,必须将基类的析构函数声明为虚拟的(否则,通过其基类的指针删除子类的实例将无法正确链接析构函数) 我假设,即使是一个空的析构函数也会阻止编译器自动生成一个特殊的移动函数。例如: class Base { virtual ~Base() { } }; 但是,可以默认使用析构函数,如中所示: class

C++11中自动生成特殊移动函数(构造函数和赋值运算符)的规则规定不能声明析构函数。其逻辑大概是,如果你需要在销毁中做一些特殊的事情,那么移动可能不安全

但是,为了在多态性中正确调用析构函数,必须将基类的析构函数声明为虚拟的(否则,通过其基类的指针删除子类的实例将无法正确链接析构函数)

我假设,即使是一个空的析构函数也会阻止编译器自动生成一个特殊的移动函数。例如:

class Base {
    virtual ~Base() { }
};
但是,可以默认使用析构函数,如中所示:

class Base {
    virtual ~Base() = default;
}
问题1:这是否允许编译器自动生成特殊的移动函数

但是,显式默认析构函数有一个问题。至少在GCC 4.8.2的情况下,签名被隐式更改为noexcept。例如:

class Base {
    virtual ~Base() = default; // compiler changes to:
    // virtual ~Base() noexcept;
}
虽然我对析构函数中的noexcept没有问题,但这会破坏以下“客户端”代码:

因此,问题2更为关键:是否有一种方法允许在C++11中自动生成特殊的移动函数,并允许正确的析构函数链接到子类(如上所述),而不破坏子类(“客户机”)代码

  • 不,默认析构函数仍然被认为是用户定义的,因此它将阻止移动操作的生成。同时声明移动操作
    default
    -ed以使编译器生成它们

  • 您只需要在基类中将移动操作声明为
    default
    -ed。在派生类中,析构函数不再是用户定义的(除非您明确地这样说),因此不会删除移动操作

  • 所以我要做的是:

    class Base
    {
        virtual ~Base() = default;
        Base(Base&&) = default;
        Base& operator=(Base&&) = default;
        // probably need to think about copy operations also, as the move disables them
        Base(const Base&) = default;
        Base& operator=(const Base&) = default;
    };
    
    我强烈推荐这篇演讲,作者可能是对move语义学贡献最大的人:

    或者,如果你能动手,你<强>应该< /强>阅读项目17:了解Scott Meyers优秀书《现代C++》中的特殊成员函数生成。这个问题解释得很好

    PS:我认为你应该多考虑一下你的基类。大多数情况下,您应该使用抽象类,因此不需要复制/移动它们的实例

    PSS:我认为默认情况下,C++11/14中的析构函数标记为
    noexcept
    ,因此不显式指定它不会导致任何问题:

    继承构造函数和隐式声明的默认值 构造函数、复制构造函数、移动构造函数、析构函数、, 复制分配运算符、移动分配运算符都是 默认情况下为noexcept(true),除非需要调用函数 这就是noexcept(false),在这种情况下,这些函数是 无例外(错误)


    为什么
    ~Sub
    更松散?无异常规范的析构函数默认为
    noexcept
    。这个问题似乎定义得很好,但出于兴趣,您是否有一个示例说明为什么需要这样做?这似乎是值和引用语义的奇怪混合。在我看来,如果你想使用移动(甚至复制),你就不会想要多态行为。更重要的是,默认的复制或移动肯定不会有任何多态行为。@tahsmith的目标是以这样一种方式重新编写基类,使它们及其子类能够利用移动语义而不破坏子类。@Pradhan当我尝试这样做时,GCC产生了关于松散定义的错误。@Pradhan顺便说一句,这是不对的,请看这里:谢谢,我认为这解决了问题(并回答了问题)。当我想到这个问题时,我实际上正在阅读斯科特书中的第17条:)@stephelton很高兴这有帮助。关于“looser nothrow”规范,我想我以前碰到过这个(g++4.8.x),这是一个编译器问题,我将尝试找到链接并发布它here@stephelton找到它,请参见此处:
    class Base
    {
        virtual ~Base() = default;
        Base(Base&&) = default;
        Base& operator=(Base&&) = default;
        // probably need to think about copy operations also, as the move disables them
        Base(const Base&) = default;
        Base& operator=(const Base&) = default;
    };