提问人:John 提问时间:6/11/2022 最后编辑:John 更新时间:6/11/2022 访问量:157
将派生类标记为可移动而基类不可移动是否有意义\合适?
Is it meaningful\suitable to mark the derived class as movable while the base class is non-moveable?
问:
将派生类标记为可移动而基类不可移动是否有意义\合适?
我知道这种不一致在 C++ 中是合法的,但它在实践中有意义\合适吗?
一般来说,我应该小心翼翼地保持这种一致性吗?
这种情况如何:当我打算将派生类标记为不可移动和不可复制时,我是否也应该将基类标记为不可移动和不可复制?
我做了几个测试来弄清楚。
这是第一个示例。由于基类是不可复制和不可移动的,因此派生类实际上是不可移动的,因为它有一个移动构造函数,这在我的期望中。提示:下面的代码片段无法编译。
#include <memory>
#include <string>
#include <iostream>
class Base {
public:
Base(){}
Base(const Base&) = delete;
Base(Base&&) = delete;
Base& operator=(const Base&) = delete;
Base& operator=(Base&&) = delete;
};
class Derived:public Base
{
public:
Derived(){}
Derived(const Derived&) = default;
Derived(Derived&&) = default;
Derived& operator=(const Derived&) = default;
Derived& operator=(Derived&&) = default;
};
int main()
{
Derived derived;
Derived derived1{std::move(derived)};
}
这是第二个示例。基类是可复制且不可移动的,但派生类实际上是可移动的,因为当调用派生类的移动构造函数时,它将调用基类的复制构造函数而不是基类的移动构造函数,这也是我的期望。提示:下面的代码片段效果很好。
#include <memory>
#include <string>
#include <iostream>
class Base {
public:
Base(){}
Base(const Base&) = default;
Base(Base&&) = delete;
Base& operator=(const Base&) = default;
Base& operator=(Base&&) = delete;
};
class Derived:public Base
{
public:
Derived(){}
Derived(const Derived&) = default;
Derived(Derived&&) = default;
Derived& operator=(const Derived&) = default;
Derived& operator=(Derived&&) = default;
};
int main()
{
Derived derived;
Derived derived1{std::move(derived)};
}
更新时间:
多亏了@Nightlord,他发现上面的代码片段不能用 Clang
编译,这真的出乎我的意料。
答:
1赞
Nightlord
6/11/2022
#1
我知道这种不一致在C++中是合法的,但事实确实如此 在实践中有意义吗?
一般来说,我应该小心翼翼地保持这种一致性吗?
是的,我认为你应该保持一致性。 若要使派生类可移动而基类不可移动,则基类不能包含任何数据成员,否则移动不会完成。
如果您的类不包含任何数据成员,则可能将其用作接口。移动构造函数不适用于这种情况,因为客户端只能识别接口(基类)而不是具体类(派生类)。
评论
0赞
John
6/11/2022
你的第二段对我来说是新的。谢谢。我将做一些测试以完全理解它。“要使派生类可移动,而基类不可移动,基类不能保存任何数据成员,否则移动未完成”是什么意思?即使基类中存在成员变量,它也确实有效。
0赞
Nightlord
6/11/2022
@John您好,在您的示例中,derived1 真正调用的是“Copy Constructor”而不是“Move Constructor”。Move 构造函数将不起作用,因为“str”属性由 Base 类维护,并且 Base 的 Move 构造函数被删除。因此,“str”是不可移动的。以下是您的示例的两个简单修改版本,它们显示了我所说的内容。1. 删除基类 2 的复制构造函数后无法编译。str 不会移动,而是复制到 derived1。Movable 类显示了它应该如何工作
0赞
Nightlord
6/11/2022
我不确定为什么它调用复制构造函数而不是编译错误。我猜这是由编译器完成的,因为某些编译器在编译示例时显示编译错误。所以我不认为这是 C++ 标准中的默认行为。
0赞
John
6/11/2022
这两个示例代码片段的行为确实是例外。一些编译器在编译我的示例时显示编译错误?您能否用在线编译器重现这种现象?据我所知,应该没有任何错误。
1赞
Nightlord
6/11/2022
下面是一个示例。我只是在您提供的链接上选择另一个编译器。
评论
Base
std::move
std::optional
。在 C++20 概念之前,有条件删除是通过有条件地从不同的基类派生来实现的。其中一个基类将删除复制/移动构造函数,另一个基类将定义它。然后,最派生的类(例如)只是默认它(这意味着它要么被删除,要么不被删除)。但这不是典型的继承。std::optional