【问题标题】:Is a self-assignment check really required while implementing move assignment operator for a class? [duplicate]在为类实现移动赋值运算符时真的需要进行自赋值检查吗? [复制]
【发布时间】:2019-05-03 21:37:09
【问题描述】:
Type &Type::operator=(Type &&rhs)
{
if(this == &rhs) //is there any need of self-assignment . 
returh *this ;
}
...
}

//既然会在r-value上调用,那为什么要自赋值呢??

【问题讨论】:

  • Type a; a = std::move(a); 从技术上讲,它在任何时候都不是必需的,但在 Rule-of-[35]-subject 课程中非常有用,以低廉的价格获得一致性。
  • 使用来自 bipll 的代码 sn-p 您可以自己弄清楚。编写一个管理int* 的小类,并为其实现移动分配,而不检查自分配。它有多无错误?
  • 如果我们编写一个具有复制赋值运算符和移动赋值运算符的类。所以在一个(复制或移动)运算符中进行自分配检查就足够了。而不是同时签入(复制和移动)。
  • 对于 stl,未指定自移动分配。请参阅此answer(由顶级 c++ 委员会成员和 libc++ 实施者撰写)
  • 我的意见是像在 STL 中那样做。但是你可以断言:assert(this!=&rhs) 看看会发生什么。否则,您可以指定它具有默认语义,例如重置对象。

标签: c++ c++11 move-semantics move-assignment-operator


【解决方案1】:

通常...

假设你的类拥有一个指向它分配的缓冲区的指针。现在,在一个幼稚的移动赋值运算符中,你会:

  • 释放您自己的缓冲区
  • 将另一个对象的缓冲区指针分配给您的缓冲区指针
  • 将 null 分配给另一个对象的缓冲区指针,并可能将其大小设置为 0

这不会使您取消对空指针的引用,但是 - 您刚刚丢失了缓冲区中的所有数据,这可能不是您想要的,也不是用户期望的。

...但并非总是如此

上面的规则有一个(狭窄的)例外:移动赋值运算符对于自赋值是“幂等的”的情况。例如,如果您的赋值运算符仅涉及成员的赋值 - 那么自赋值就像常规赋值一样是安全的(相信成员的自赋值实现是有效的)。什么都不会改变或丢失。

该规则的例外情况确实很窄,因为以上对于我给出的示例大部分都是正确的——在这种情况下,您将使用默认的移动赋值运算符。不过,仅仅因为您没有在某人的代码中发现此检查并不意味着存在错误。

【讨论】:

  • "您刚刚丢失了缓冲区中的所有数据。"这通常不是一件坏事(你有任何情况,这实际上是一个问题吗?)。标准并没有说必须保留数据:eel.is/c++draft/utility.arg.requirements#tab:moveassignable。实际上,例如,将std::vector 移动到自身将删除其内容(libc++ 和 stdlibc++)。
  • @geza:总是。在将y 分配给x 之后,您希望x 具有与之前y 相同的值(移动或不移动)。
  • 另一方面,当您从y 移动时,其内容为空也就不足为奇了。因此,在这种情况下存在相互矛盾的期望。这就是为什么我要问,这实际上意味着什么问题?例如,“移动”swap 仍然有效 (tmp = move(a); a = move(b); b = move(tmp))。是否有任何算法,这种行为(数据丢失)会导致问题?标准不要求在这种情况下保留数据一定是有原因的。
  • @geza 有趣的参考:stackoverflow.com/a/9322542/5632316
  • @Oliv:他说:“对于移动分配:x = std::move(y); 应该有一个后置条件,即 y 具有有效但未指定的状态。”但是还有另一个合理的后置条件:x 应该等于分配之前的y。它被违反了。这通常不会导致任何问题(我还没有找到任何案例)。但这仍然可能出乎意料。
猜你喜欢
  • 2017-11-21
  • 2020-05-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-19
  • 2015-06-23
  • 1970-01-01
  • 2011-08-02
相关资源
最近更新 更多