我试图找出为什么在下面的例子中,当我禁用复制语义但保留移动语义时,将其放置到 std::vector 在一种情况下有效,但在另一种情况下(当使用继承时)无效:
#include <iostream>
#include <vector>
class NonCopyable
{
public:
NonCopyable(const NonCopyable &) = delete;
NonCopyable(NonCopyable &&) noexcept = default;
NonCopyable &operator=(const NonCopyable &) = delete;
NonCopyable &operator=(NonCopyable &&) noexcept = default;
NonCopyable() = default;
virtual ~NonCopyable() = default;
};
// NOTE: here things works as long as I dont override the destructor. If I do, it stops.
class MyClass : public NonCopyable
{
public:
MyClass() = default;
// NOTE: when commented out, all compiles fine, otherwise not
~MyClass() override {}
};
// NOTE: when all is put into a single class, everything compiles ok
class MyClass2
{
public:
MyClass2() = default;
~MyClass2() noexcept
{
}
MyClass2(const MyClass2 &) = delete;
MyClass2 &operator=(const MyClass2 &) = delete;
MyClass2(MyClass2 &&) noexcept = default;
MyClass2 &operator=(MyClass2 &&) noexcept = default;
};
int main()
{
std::vector<MyClass> mc;
MyClass a;
mc.emplace_back(std::move(a));
std::vector<MyClass2> mc2;
MyClass2 a2;
mc2.emplace_back(std::move(a2));
}
字符串
在线编译器中的示例:https://onlinegdb.com/UTWju9WkU
我错过了什么?谢谢你,谢谢
1条答案
按热度按时间pcww981p1#
5的规则指出,如果你触摸5个特殊成员函数(复制ctor,复制赋值,移动ctor,移动赋值,dtor)中的任何一个,你应该触摸它们。不这样做是一个糟糕的计划。(5的规则也被称为3的规则,从后退到移动操作存在)。
在这种情况下,当您实现
~MyClass()
时,编译器不再合成您的移动构造函数。emplace_back
需要使用move构造函数,因为如果调整了向量的大小,就会用到它。标准有确切的规则,但你不需要记住它们。你只需要记住5的规则。
(The这里要知道的另一条规则是0规则--除非您正在编写资源管理类,否则不要碰它们。如果你正在编写一个资源管理类,它应该只做资源管理;使用组合将资源存储在业务逻辑类中。然后,您的业务逻辑类遵循0规则,并从其成员获得其移动/复制/销毁语义。)