简体   繁体   English

有自我分配有用的情况吗?

[英]Are there situations when self-assignment is useful?

It is commonly known that when implementing an assignment operator one has to protect against self-assignment, at least when the class has non-POD members. 众所周知,当实现赋值运算符时,必须防止自我赋值,至少当该类具有非POD成员时。 Usually it is (or is equivalent to): 通常是(或相当于):

Foo& operator=(const Foo& other)
{
  if (&other == this)
     return *this;
  ... // Do copy
}

What were the reasons for not inserting the self-assignment protection automatically? 不自动插入自我分配保护的原因是什么? Are there use cases when self-assignment does something non-trivial and practically useful? 当自我指派做一些非常重要且实际有用的事情时,是否有用例?

Foo& operator=(const Foo& other)
{
  if (&other == this)
  {
    // Do something non-trivial
  }
  else
  {
    // Do copy
  }
  return *this;
}

To summarize the answers and discussion by now 总结一下现在的答案和讨论

Looks like non-trivial self-assignment can never be really useful. 看起来非平凡的自我分配永远不会真正有用。 The only option proposed was to put an assert there in order to detect some logical errors. 建议的唯一选择是在那里放置一个assert ,以便检测一些逻辑错误。 But there are quite legitimate self-assignment cases like a = std::min(a, b) , so even this option is highly dubious. 但是有一些非常合理的自我赋值案例,例如a = std::min(a, b) ,所以即使这个选项也是非常可疑的。

But there are two possible implementations of a trivial self-assignment: 但是,有两种可能的简单自我赋值实现:

  1. Do nothing if &other == this . 如果&other == this做什么都不&other == this Always work, though may have negative performance impact due to an extra branching. 始终工作,但由于额外的分支可能会对性能产生负面影响。 But in a user-defined assignment operator the test must be almost always explicitly made. 但是在用户定义的赋值运算符中,必须几乎总是明确地进行测试。
  2. Copy each member to itself. 将每个成员复制到自己。 This is what is done by default. 这是默认情况下完成的操作。 If the members use default assignment operators as well, it may be faster, because doesn't requre an extra branching. 如果成员也使用默认赋值运算符,则可能更快,因为不需要额外的分支。

I still don't see why the C++ standard could not guarantee that in a user-defined assignment operator &other != this . 我仍然不明白为什么C ++标准不能保证在用户定义的赋值运算符&other != this If you want no branching, use the default operator. 如果您不想分支,请使用默认运算符。 If you are redefining the operator, some test is needed anyway... 如果您要重新定义操作员,无论如何都需要进行一些测试......

Self-assignment protection is only necessary for types where the code being skipped is dangerous when applied to itself. 仅当被跳过的代码在应用于自身时是危险的类型时,才需要自我分配保护。 Consider the case where you have a user-provided assignment operator because each individual object has some kind of identifier, which you don't want to copy. 考虑具有用户提供的赋值运算符的情况,因为每个单独的对象都有某种标识符,您不想复制该标识符。 Well, you can "copy" the other values just fine in self-assignment cases. 好吧,你可以在自我分配案例中“复制”其他值。 So inserting an invisible self-assignment test is just adding a pointless and potentially costly conditional branch. 因此,插入一个不可见的自我指派测试只是添加一个毫无意义且可能成本很高的条件分支。

So it's not about self-assignment being useful; 所以这不是关于自我指派有用的; it's about self-assignment not always needing protection. 这是关于自我分配并不总是需要保护。

Furthermore, C++ generally doesn't like adding code like that to your code without you explicitly asking for it. 此外,C ++通常不喜欢在没有明确要求的情况下将代码添加到代码中。 It's typically done in terms of whole functions, not part of functions. 它通常是在整个功能方面完成的,而不是功能的一部分。 Even destructor calls at the end of blocks are something you asked for when you put the object to be destroyed on the stack. 甚至在块末尾的析构函数调用都是在将要销毁的对象放在堆栈上时要求的。

There are algorithms where it can happen. 有算法可以发生。

  1. You know the lhs and rhs might be the same but it is just simpler to do the assignment than check. 你知道lhs和rhs可能是相同的但是做分配比检查更简单。 Eg, consider a = std::min(a,b); 例如,考虑a = std::min(a,b); - simpler and perhaps easier to understand than if (a > b) a = b; - 比if (a > b) a = b;更简单,也许更容易理解if (a > b) a = b; - now consider more complicated examples of similar things. - 现在考虑类似事情的更复杂的例子。

  2. You don't know whether lhs and rhs might be the same, because they may have been passed in from somewhere else. 你不知道lhs和rhs是否相同,因为它们可能是从其他地方传来的。

These algorithms where it can happen are not uncommon. 这些可能发生的算法并不罕见。

I should admit I have never heard about the common knowledge like this. 我应该承认我从未听说过像这样的常识。 For a non-POD objects, a more strict approach is to forbid copying them with disabling copy constructor and the assignment operator. 对于非POD对象,更严格的方法是禁止使用禁用复制构造函数和赋值运算符复制它们。 So that you don't have the problem at all. 这样你根本就没有问题。

If you still need to copy the class, but there is some data which is unsafe to copy to itself, you could only override assignment for that data, and when it is used as a field, it would be used by an automatic assignment implementation of the upper level class. 如果您仍然需要复制该类,但有一些数据不安全,无法复制到自身,您只能覆盖该数据的赋值,当它用作字段时,它将被自动赋值实现使用上层阶级。

As for your question, if you only need to skip doing anything the automatic "self-assignment protection" is there already, in a way. 至于你的问题,如果你只需要跳过任何事情,那么在某种程度上已经存在自动“自我分配保护”。 If you don't define assignment operation explicitely and let compiler use the automatic one, after inlining self-assignment may become no-op. 如果没有明确定义赋值操作并让编译器使用自动赋值操作,则在内联自赋值后可能会变为无操作。

For example, the following code: 例如,以下代码:

class A {
    int a;
    double b;
};

A& foo(A& input)
{
    return (input = input);
}

is compiled to (gcc 4.9, -O2): 编译为(gcc 4.9,-O2):

_Z3fooR1A:
    .cfi_startproc
    movq    %rdi, %rax
    ret
    .cfi_endproc

Which does not copy anything. 哪个不复制任何东西。

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

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