C++ 添加自定义析构函数时,移动构造函数在派生类中消失

C++ 添加自定义析构函数时,移动构造函数在派生类中消失,c++,inheritance,destructor,move-semantics,C++,Inheritance,Destructor,Move Semantics,我有一个只移动的基类和一个继承基类构造函数的派生类。我想给派生的构造函数一个自定义析构函数,但当我这样做时,它不再继承Base的move构造函数。非常神秘。发生了什么事 移动构造函数不是通过使用Base::Base继承的按照您似乎认为的方式,因为Base中的移动构造函数没有Derived中的移动构造函数应有的签名。前者采用基&,后者采用派生& 然后在Derived中声明析构函数。这将禁止对派生的隐式声明移动构造函数。因此,派生的中没有移动构造函数 然后,编译器返回到派生的隐式生成的复制构造函数

我有一个只移动的基类和一个继承基类构造函数的派生类。我想给派生的构造函数一个自定义析构函数,但当我这样做时,它不再继承Base的move构造函数。非常神秘。发生了什么事


移动构造函数不是通过使用Base::Base继承的
按照您似乎认为的方式,因为
Base
中的移动构造函数没有
Derived
中的移动构造函数应有的签名。前者采用
基&
,后者采用
派生&

然后在
Derived
中声明析构函数。这将禁止对
派生的
隐式声明移动构造函数。因此,
派生的
中没有移动构造函数

然后,编译器返回到
派生的
隐式生成的复制构造函数,用于
派生的d2=std::move(d)。但它被定义为已删除,因为
派生的
的基类不可复制。(您手动删除了
Base
s复制构造函数。)

在重载解析中,删除的副本构造函数是在继承的基类
Base(Base&&)
构造函数之上选择的(尽管
派生的
右值可以绑定到
Base&&
),因为后者需要一个不被认为完全匹配的转换序列,当绑定到一个
常量派生时&
被认为是完全匹配的,以便解决重载问题

此外,还有建议的解决方案措辞,将继承的
Base
move构造函数完全排除在重载解决方案之外。(据我所知,这是编译器已经实现的。)

如果您没有很好的理由声明析构函数,请不要这样做。如果确实有原因,则需要再次默认移动操作,就像在
Base
中对移动构造函数所做的那样。(如果类应该是可分配的,您可能也希望默认使用移动分配操作符。)


如果要以多态方式使用类层次结构,则应在多态基中声明虚拟(默认)析构函数,但不需要在派生类中声明析构函数。

移动构造函数是在特定情况下生成的

在创建析构函数时,您已停止编译器生成移动构造函数


此外,如果没有虚拟基析构函数,请创建一个虚拟基析构函数。如果它不需要做任何特殊的事情,则默认它。与基本移动构造函数相同,只是不要将其保留为空,将其声明为默认值。您正在使用
=delete
,同时使用
=default

继承的移动构造函数没有派生类的签名

在第一种情况下,如果没有显式声明的析构函数,编译器将隐式声明派生类的默认移动构造函数

在第二种情况下,当显式声明析构函数时,编译器不会隐式声明move构造函数

< >从C++ 17标准({复制)/移动构造函数)

8如果X类定义未明确声明移动 构造函数,非显式构造函数将隐式声明为 当且仅当

(8.1)X没有用户声明的副本构造函数

(8.2)X没有用户声明的副本分配运算符

-(8.3)X没有用户声明的移动分配运算符,并且

-(8.4)X没有用户声明的析构函数。


但在任何情况下,由于签名不同,基类的move构造函数都不是派生类的move构造函数。

可能会给
派生的
一个move构造函数?如果派生类有任何数据成员,则基类移动构造函数将不正确。您的基类没有虚拟析构函数。不确定这是否是问题所在,但它似乎可疑。
派生的&&
可以隐式转换为
基本的&&
,因此似乎还没有解释为什么
派生的d2=std::move(d)
不能执行
B(B&&
。错误消息似乎表明重载解析正在选择
D(D const&)=delete
over
B(B&&)
@M.M,因为这与重载分辨率的匹配更差。它需要转换。
// move-only
struct Base {
    Base() = default;
    Base(Base const &) = delete;
    Base(Base &&) {}
};

struct Derived : public Base {
    using Base::Base;

    // remove this and it all works
    ~Derived() { /* ... */ }
};

int main() {
    Base b;
    // works
    Base b2 = std::move(b);

    Derived d;
    // fails
    Derived d2 = std::move(d);
}