让我们看看一些简单的移动可构造和(非简单)可复制构建(但仍可复制构造)的用户定义(类)类型A
:
struct A
{
A() = default;
A(A const &) {}
A(A &&) = default;
};
然后移动A
(移动构造或移动分配)字面意思地执行以下操作:源按位复制到目的地,尽管操作名称为“移动”。在琐碎的移动右侧(正式)不是const
,但整个操作的琐碎需要(实际)右手边的不可变性,不是吗?在我看来,这意味着,琐碎的复制操作和琐碎的移动操作在它们的深层性质(在内存,内存布局,位等方面)完全相同。我是对的吗?
如果是这样,那么我认为,如果我在用户代码中看到简单的可移动构造,但不是简单的可复制构造类型,那么我显然会看到一些反模式。我是对的吗?
是否有一个这样的人工但可用的类型的例子,它不是简单的可复制构造/可分配的,而是简单的可移动构造/可分配的吗?
答案 0 :(得分:2)
是否有一个用例,其中一个类型可能有一个简单的复制构造函数而没有一个简单的移动构造函数?肯定。
例如,有一个指针包装器类型在移动时始终为空可能很有用。复制构造函数没有理由不重要,但移动构造函数必须将旧值设置为NULL。
template<typename T>
class empty_on_move
{
T *ptr_;
public:
empty_on_move(const empty_on_move&) = default;
empty_on_move(empty_on_move &&other) : ptr_(other.ptr_) {other.ptr_ = nullptr;}
...
};
empty_on_move
不拥有该对象,这就是拥有它的多个副本的原因。它的存在只是为了确保当你从它移动时,指针处于一个易于理解的状态。因此,is_trivially_copy_constructible<empty_on_move<T>>
为真,而is_trivially_move_constructible<empty_on_move<T>>
为假。
它主要用于其他想要指出特定行为的类。这样,您就不必将代码显式地写入其移动构造函数/赋值中以将这些字段用于NULL。
话虽如此,你真的在问错误的问题。为什么?因为答案无关紧要。
复制/移动构造函数/赋值的重要性的唯一时间是当您需要类型为Trivially Copyable时。 属性允许使用memcpy
这样的东西,而不是个别操作的平凡。简单的可复制属性要求复制/移动构造函数/赋值和析构函数所有都是微不足道的(或者从C ++ 14开始删除)。
如果您正在编写某种类型的包装器(或编写sum / product类型),并且您想要公开该类型的属性,那么您只需要关注暴露Trivial Copyability。也就是说,如果T
(或Ts...
)可以轻易复制,那么您的类型也应该是可以复制的。
但是除此之外,你不应该只因为T
而需要一个简单的复制构造函数。