添加自定义析构函数时,移动构造函数在派生 class 中消失
Move constructor disappears in derived class when adding custom destructor
我有一个只能移动的 Base class 和一个继承 Base 构造函数的 Derived。我想给 Derived 一个自定义析构函数,但是当我这样做时它不再继承 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
中的移动构造函数没有 [= 中的移动构造函数的签名12=] 会有。前者取一个Base&&
,后者取一个Derived&&
.
然后在 Derived
中声明一个析构函数。这会禁止 Derived
的移动构造函数的隐式声明。所以 Derived
.
中没有移动构造函数
然后编译器回退到 Derived
为 Derived d2 = std::move(d);
隐式生成的复制构造函数。但这被定义为已删除,因为 Derived
的基础 class 不可复制。 (您手动删除了 Base
的复制构造函数。)
在重载决议中,删除的复制构造函数在 Base classes 继承的 Base(Base&&)
构造函数上被选择(尽管 Derived
右值可以绑定到 Base&&
),因为后者需要不被视为 完全匹配 的转换序列,而绑定到 const Derived&
被视为 完全匹配 目的重载决议。
还有针对 CWG issue 2356 决议的拟议措辞,这将完全排除继承的 Base
移动构造函数参与重载决议。 (据我所知,这是编译器已经实现的。)
如果您没有充分的理由声明析构函数,请不要这样做。如果你确实有理由,那么你需要再次默认移动操作,就像你在 Base
中对移动构造函数所做的那样。 (如果 classes 应该是可赋值的,您可能还想默认移动赋值运算符。)
如果您打算以多态方式使用 class 层次结构,您应该在多态基类中声明一个虚拟(默认)析构函数,但不需要在派生 class 中声明一个析构函数es.
移动构造函数是在特定情况下生成的。
https://en.wikipedia.org/wiki/Special_member_functions
在创建析构函数时,您停止了编译器生成移动构造函数。
此外,如果您没有虚拟 Base 析构函数,请创建一个。如果它不需要做任何特别的事情,就默认它。与您的 Base 移动构造函数相同,只是不要将其留空,将其声明为默认值。您正在使用 =delete
,也请使用 =default
。
继承的移动构造函数没有派生的签名class。
在第一种没有明确声明的析构函数的情况下,编译器隐式声明派生的默认移动构造函数 class。
在第二种情况下,当显式声明析构函数时,编译器不会隐式声明移动构造函数。
来自 C++ 17 标准(15.8.1 Copy/move 构造函数)
8 If the definition of a class X does not explicitly declare a move
constructor, a non-explicit one will be implicitly declared as
defaulted if and only if
(8.1) X does not have a user-declared copy constructor,
(8.2) X does not have a user-declared copy assignment operator,
—(8.3) X does not have a user-declared move assignment operator, and
> —(8.4) X does not have a user-declared destructor.
但是在任何情况下,基 class 的移动构造函数都不是派生 class 的移动构造函数,因为签名不同。
我有一个只能移动的 Base class 和一个继承 Base 构造函数的 Derived。我想给 Derived 一个自定义析构函数,但是当我这样做时它不再继承 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
中的移动构造函数没有 [= 中的移动构造函数的签名12=] 会有。前者取一个Base&&
,后者取一个Derived&&
.
然后在 Derived
中声明一个析构函数。这会禁止 Derived
的移动构造函数的隐式声明。所以 Derived
.
然后编译器回退到 Derived
为 Derived d2 = std::move(d);
隐式生成的复制构造函数。但这被定义为已删除,因为 Derived
的基础 class 不可复制。 (您手动删除了 Base
的复制构造函数。)
在重载决议中,删除的复制构造函数在 Base classes 继承的 Base(Base&&)
构造函数上被选择(尽管 Derived
右值可以绑定到 Base&&
),因为后者需要不被视为 完全匹配 的转换序列,而绑定到 const Derived&
被视为 完全匹配 目的重载决议。
还有针对 CWG issue 2356 决议的拟议措辞,这将完全排除继承的 Base
移动构造函数参与重载决议。 (据我所知,这是编译器已经实现的。)
如果您没有充分的理由声明析构函数,请不要这样做。如果你确实有理由,那么你需要再次默认移动操作,就像你在 Base
中对移动构造函数所做的那样。 (如果 classes 应该是可赋值的,您可能还想默认移动赋值运算符。)
如果您打算以多态方式使用 class 层次结构,您应该在多态基类中声明一个虚拟(默认)析构函数,但不需要在派生 class 中声明一个析构函数es.
移动构造函数是在特定情况下生成的。
https://en.wikipedia.org/wiki/Special_member_functions
在创建析构函数时,您停止了编译器生成移动构造函数。
此外,如果您没有虚拟 Base 析构函数,请创建一个。如果它不需要做任何特别的事情,就默认它。与您的 Base 移动构造函数相同,只是不要将其留空,将其声明为默认值。您正在使用 =delete
,也请使用 =default
。
继承的移动构造函数没有派生的签名class。
在第一种没有明确声明的析构函数的情况下,编译器隐式声明派生的默认移动构造函数 class。
在第二种情况下,当显式声明析构函数时,编译器不会隐式声明移动构造函数。
来自 C++ 17 标准(15.8.1 Copy/move 构造函数)
8 If the definition of a class X does not explicitly declare a move constructor, a non-explicit one will be implicitly declared as defaulted if and only if
(8.1) X does not have a user-declared copy constructor,
(8.2) X does not have a user-declared copy assignment operator,
—(8.3) X does not have a user-declared move assignment operator, and
> —(8.4) X does not have a user-declared destructor.
但是在任何情况下,基 class 的移动构造函数都不是派生 class 的移动构造函数,因为签名不同。