添加自定义析构函数时,Move 构造函数在派生类中消失



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

戈德博尔特

// 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);
}

移动构造函数没有像您认为的那样与using Base::Base;继承,因为Base中的移动构造函数没有Derived中的移动构造函数所具有的签名。前者取Base&&,后者取Derived&&

然后在Derived中声明析构函数。这禁止了Derived的移动构造函数的隐式声明。所以Derived中没有移动构造函数。

然后编译器回退到Derived隐式生成的复制构造函数以进行Derived d2 = std::move(d);。但这被定义为已删除,因为Derived的基类不可复制。(您手动删除了Base复制构造函数。

在重载解析中,删除的副本构造函数被选择在构造函数继承Base(Base&&)基类上(尽管Derivedrvalue可以绑定到Base&&(,因为后者需要不被视为完全匹配的转换序列,而绑定到const Derived&被认为是完全匹配以实现重载解析的目的。

此外,还有用于解决 CWG 问题 2356 的拟议措辞,该措辞将完全排除继承的Base移动构造函数参与重载解析。(据我所知,这是编译器已经实现的内容。

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

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

Move 构造函数在特定情况下生成。

https://en.wikipedia.org/wiki/Special_member_functions

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

此外,如果没有虚拟 Base 析构函数,请创建一个虚拟 Base 析构函数。如果它不必执行任何特殊操作,则默认它。与您的 Base 移动构造函数相同,只是不要将其留空,将其声明为默认值。您正在使用=delete,也使用=default

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

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

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

从 C++ 17 标准(15.8.1 复制/移动构造函数(

8 如果类 X 的定义没有明确声明移动 构造函数,一个非显式的将被隐式声明为 默认当且仅当

(8.1( X 没有用户声明的复制构造函数,

(8.2( X 没有用户声明的复制赋值运算符,

—(8.3( X 没有用户声明的移动赋值运算符,并且

> —(8.4( X 没有用户声明的析构函数。

但无论如何,基类的 ,move 构造函数不是派生类的移动构造函数,因为签名不同。

最新更新