简体   繁体   English

根据析构函数和移动构造函数实现移动赋值

[英]Implementing move assignment in terms of destructor and move constructor

Say I have a class which manages memory and thus needs user-defined special member functions (imagine vector or similar). 假设我有一个管理内存的类,因此需要用户定义的特殊成员函数(想象vector或类似)。

Consider the following implementation of the move-assignment operator: 考虑move-assignment运算符的以下实现:

Class& operator=(Class&& rhs)
{
    this->~Class();                    // call destructor
    new (this) Class(std::move(rhs));  // call move constructor in-place
}
  1. Is it valid to implement a move-assignment operator this way? 以这种方式实现移动赋值运算符是否有效 That is, does calling a destructor and constructor in this way not run afoul of any object lifetime rules in the language? 也就是说,以这种方式调用析构函数和构造函数是否与语言中的任何对象生存规则相冲突?

  2. Is it a good idea to implement a move-assignment operator this way? 以这种方式实现移动赋值运算符是一个好主意吗? If not, why not, and is there a better canonical way? 如果没有,为什么不,并且有更好的规范方式?

It's not valid: What if this move assignment is called as part of moving a child object? 它无效:如果移动子对象的一部分调用此移动赋值怎么办? Then you destroy the child (assuming it has a virtual destructor) and recreate in its place a parent object. 然后你销毁孩子(假设它有一个虚拟的析构函数)并在其位置重新创建一个父对象。

I would say that even in a non-virtual context it's still a bad idea because you don't see the syntax very often and it may make the code harder to grok for future maintainers. 我会说,即使在非虚拟环境中,它仍然是一个坏主意,因为你不经常看到语法,它可能会使代码更难以为未来的维护者。

The best approach is to avoid having to write your own move constructor entirely (and using the default) by having all your class members take care of moving themselves. 最好的方法是避免必须完全编写自己的移动构造函数(并使用默认值),让所有的类成员都自己动手。 For example rely on unique_ptr, etc. Failing that it seems that implementing it in terms of swap (as the copy-and-swap for copy assignment) would be an easily understandable mechanism. 例如,依赖unique_ptr等。如果失败的话,似乎在交换方面实现它(作为复制分配的复制和交换)将是一个易于理解的机制。

  1. It may be valid(1). 它可能是有效的(1)。 To address your specific issue about dtor/ctor lifetimes, yes that is valid(2). 为了解决有关dtor / ctor生命周期的具体问题,是有效的(2)。 That is how the original implementation for vector worked. 这就是矢量的原始实现如何工作。
  2. It may be a good idea (it probably isn't), but you may not want a canonical way.(3) 这可能是一个好主意(可能不是),但你可能不想要一个规范的方式。(3)

(1) There is controversy about whether or moves need to be valid in the self-move case. (1)关于在自我移动案件中是否需要有效或者移动是否有效存在争议。 Arguing for self-move safety is the position that code should be safe (duh), we certainly expect self assignment to be safe. 争论自我安全是代码应该是安全的(duh),我们当然希望自我分配是安全的。 Also some user experience reports that for many algorithms which use move, self-move is possible and tedious to check for. 此外,一些用户体验报告说,对于许多使用移动的算法,自动移动是可能的并且很难检查。

Arguing against self-move safety is the position that the whole point of move semantics is time savings and for that reason moves should be as fast as possible. 反对自动安全的立场是整个移动语义点是节省时间的位置,因此移动应该尽可能快。 A self-move check can be expensive relative to the cost of a move. 相对于搬家的成本,自动检查可能是昂贵的。 Note that the compiler will never generate self-move code, because natural (not casted) rvalues can't be self-moved. 请注意,编译器永远不会生成自动移动代码,因为自然(未转换)的rvalues不能自行移动。 The only way to have a self-move is if a "std::move()" cast is explicitly invoked. 拥有自动移动的唯一方法是显式调用“std :: move()”强制转换。 This puts the burden on the caller of std::move() to either verify that self-move isn't involved or convince themselves that it isn't. 这使得std :: move()调用者的负担要么验证不涉及自动移动,要么说服自己不是。 Note also that it would be trivial to create a user defined equivalent of "std::move" that checked for self-move and then did nothing. 还要注意,创建一个用户定义的等效“std :: move”来检查自动移动然后什么都不做是很简单的。 If you don't support self-move, you might want to document that. 如果您不支持自动移动,则可能需要记录该操作。

(2) This is not a template so you can know if the expression "new (this) Class(std::move(rhs));" (2)这不是模板,因此您可以知道表达式是否为“new(this)Class(s​​td :: move(rhs));” can throw. 可以投掷。 If it can, then no, this isn't valid. 如果可以,那么不,这是无效的。

(3) This code may be a puzzle to maintainers, who might expect a more traditional swap approach, but there is a potential drawbacks to the swap approach. (3)这段代码可能是维护人员的难题,他们可能期望采用更传统的交换方法,但交换方法存在潜在的缺陷。 If the resources being released by the target need to be released as soon as possible (such as a mutex), then swap has the drawback that the resources are swapped into the move source object. 如果目标释放的资源需要尽快释放(例如互斥锁),则交换的缺点是资源被交换到移动源对象中。 If the move is the result of a call to "std::move()" the move source object may not be immediately disposed of. 如果移动是调用“std :: move()”的结果,则移动源对象可能不会立即被丢弃。 (Since this isn't a template you can know what resources are being freed. If memory is the only resource being freed, then this isn't an issue.) (由于这不是模板,您可以知道正在释放哪些资源。如果内存是唯一被释放的资源,那么这不是问题。)

A better approach might be to factor out the resource freeing code from the destructor and the resource moving code from the move constructor and then just call those (inlined) routines in this move assignment operator. 一种更好的方法可能是从析构函数中分解资源释放代码,从移动构造函数中分解资源移动代码,然后在此移动赋值运算符中调用这些(内联)例程。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM