首页 > 解决方案 > 如果有成员,则基类中的默认析构函数禁用子类中的移动构造函数

问题描述

为什么默认的(用户声明的)析构函数会Base1阻止在类中生成移动构造函数/运算符,但是当我将成员从 Base ( ) 移动到 Child ( ) 类Child1时一切正常?dataBase2Child2

struct Data {
    Data() {}
    Data(Data&&) noexcept { cout << "Move constructor" << endl; }
    Data& operator=(Data&&) noexcept {
        cout << "Move assign" << endl;
        return *this;
    }
    vector<int> vec;
};

struct Base1 {
    virtual void fun() { cout << "Base1::fun" << endl; }
    virtual ~Base1() = default;
    Data data;
};

struct Child1 : public Base1 {
    void fun() override { cout << "Child1::fun" << endl; }
};

struct Base2 {
    virtual void fun() { cout << "Base2::fun" << endl; }
    virtual ~Base2() = default;
};

struct Child2 : public Base2 {
    void fun() override { cout << "Child2::fun" << endl; }
    Data data;
};

int main() {
    Child1 c1;
    auto obj1 = std::move(c1);  // error

    Child2 c2;
    auto obj2 = std::move(c2);
}

我目前的理解是,当我在 Base() 中将析构函数声明为“<code>default”时BaseDel,移动构造函数应该在 Base( BaseDel) 和 Child( ChildDel) 类中为“<code>deleted”。这个对吗?我认为,会员位置应该不重要。如果我明确地这样做,我会得到预期的错误:

struct BaseDel {
    BaseDel() {}
    virtual void fun() { cout << "BaseDel::fun" << endl; }
    BaseDel(BaseDel&& st) = delete;
    virtual ~BaseDel() = default;
};

struct ChildDel : public BaseDel {
    ChildDel() {}
    void fun() override { cout << "ChildDel::fun" << endl; }
    Data data;
};

int main() {
    ChildDel cd;
    auto objd = std::move(cd);  // OK, expected error
}

标签: c++c++11move-semanticsvirtual-destructor

解决方案


隐式移动构造函数不是(仅)被删除的,当你有一个用户声明的析构函数时,它不是首先声明的,就像Base1and的情况一样Base2

因此,在重载决议中永远不能考虑移动构造函数,因此auto obj1 = std::move(c1);,虽然它可以调用Child1的移动构造函数,但需要回退到Base1子对象的复制构造。

Base1和的隐式声明的复制构造函数Child1被定义为已删除,因为Data的隐式声明的复制构造函数被定义为已删除,因为Data具有用户定义的移动构造函数。因此auto obj1 = std::move(c1);将失败并显示隐式声明的复制构造函数被删除的错误。

因为Base2复制构造函数没有定义为已删除,因为它没有Data成员,所以auto obj2 = std::move(c2);会调用Child2' 的移动构造函数(它也使用Data' 的移动构造函数),但对子对象使用复制构造函数Base2


推荐阅读