是否存在自我分配有用的情况?

Are there situations when self-assignment is useful?

众所周知,在实现赋值运算符时,必须防止自赋值,至少当 class 具有非 POD 成员时。通常是(或等同于):

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

没有自动插入自赋保护的原因是什么?是否存在自赋值做一些重要且实际有用的用例?

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

现在总结一下答案和讨论

看起来重要的自赋值永远不会真正有用。提议的唯一选择是在那里放置一个 assert 以检测一些逻辑错误。但是像a = std::min(a, b)这样的自赋值情况是相当合法的,所以即使这个选项也是非常可疑的。

但是简单的自赋值有两种可能的实现方式:

  1. 如果 &other == this,什么也不做。始终有效,但由于额外的分支可能会对性能产生负面影响。但在用户定义的赋值运算符中,测试几乎总是必须显式进行。
  2. 将每个成员复制到自己。这是默认情况下完成的。如果成员也使用默认赋值运算符,它可能会更快,因为不需要额外的分支。

我仍然不明白为什么 C++ 标准不能保证在用户定义的赋值运算符中 &other != this。如果您不想分支,请使用默认运算符。如果您要重新定义运算符,无论如何都需要进行一些测试...

Self-assignment 保护仅对于被跳过的代码在应用于自身时是危险的类型是必要的。考虑您有一个 user-provided 赋值运算符的情况,因为每个单独的对象都有某种您不想复制的标识符。好吧,在 self-assignment 情况下,您可以 "copy" 其他值。因此,插入一个不可见的 self-assignment 测试只是添加一个毫无意义且可能代价高昂的条件分支。

所以这不是 self-assignment 有用;这是关于 self-assignment 并不总是需要保护。

此外,C++ 通常不喜欢在您没有明确要求的情况下将这样的代码添加到您的代码中。它通常是根据整个功能而不是部分功能来完成的。当您将要销毁的对象放入堆栈时,甚至块末尾的析构函数调用也是您要求的。

有可能发生这种情况的算法。

  1. 您知道 lhs 和 rhs 可能相同,但赋值比检查更简单。例如,考虑 a = std::min(a,b); - 比 if (a > b) a = b; 更简单,也许更容易理解 - 现在考虑类似事物的更复杂的例子。

  2. 你不知道 lhs 和 rhs 是否相同,因为它们可能是从其他地方传入的。

这些可能发生的算法并不少见。

我承认我从来没有听说过这样的常识。对于 non-POD 对象,更严格的方法是通过禁用复制构造函数和赋值运算符来禁止复制它们。让你完全没有问题。

如果你还需要复制class,但是有一些数据复制到自己是不安全的,你只能覆盖那些数据的赋值,当它被用作字段时,它将由上层 class.

的自动分配实现使用

至于您的问题,如果您只需要跳过任何操作,那么自动 "self-assignment protection" 在某种程度上已经存在。如果你没有明确定义赋值操作而让编译器使用自动的,在内联后 self-assignment 可能会变成 no-op.

例如下面的代码:

class A {
    int a;
    double b;
};

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

编译为 (gcc 4.9, -O2):

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

不复制任何内容。