为什么 operator = return *this?

Why does operator = return *this?

说我想覆盖 operator = 这样我就可以做类似

的事情
Poly p1;  // an object representing a polynomial
Poly p2;  // another object of the same type    
p2 = p1;  // assigns all the contents of p1 to p2

然后在我的 operator = 实现中,我有这样的东西:

Poly& Poly::operator=(const Poly &source) {
    // Skipping implementation, it already works fine…
    return *this;
}

不要介意实现,它已经运行良好。

我担心的是 return *this 时会发生什么?我知道它 returns 对对象的引用,但这是发生了什么吗?

p2 = &p1

return *this 这样你就可以编写普通的复合 C++ = 语句,例如:

Poly p1; //an object representing a polynomial
Poly p2;
Poly p2;

// ...

p3 = p2 = p1;  //assigns all the contents of p1 to p2 and then to p3

因为该语句基本上是:

p3.operator=(p2.operator=(p1));

如果 p2.operator=(...) 没有 return *this 你将没有任何有意义的东西可以传递给 p3.operator=(...)

p2 = p1p2.operator=(p1) 的 shorthand。它只是调用您的 operator= 函数,该函数返回对 p2 的引用,然后您将忽略它。为了清楚起见,我们将其称为 assign 而不是 operator=:

Poly& Poly::assign(const Poly &source) {
    .
    .
    .
    return *this;
}

现在 p2 = p1 你会写

p2.assign(p1);

在这种情况下,调用 assign 的结果将被忽略,但您不必忽略它。例如,您可以这样写:

p3.assign(p2.assign(p1));

使用 operator= 而不是 assign,这就变成了

p3 = (p2 = p1);

但由于赋值是right-associative,这也可以写成

p3 = p2 = p1;

这种能够同时进行多项赋值的形式最初来自 C,并通过在 operator=() 中返回 *this 的约定保留在 C++ 中。

返回对目标对象的引用允许赋值链接(级联),class 中的重载运算符遵循 right-associative(单击 here 了解详细的运算符重载规则)

Poly a, b, c;
a = b = c;

如果您无论如何都不需要链式赋值(如其他答案所示),则可能会想使 copy-assignment 运算符 return void 。毕竟,链式赋值通常难以阅读和理解,因此不允许它们可能被视为 改进

然而,一个经常被忽视的方面是 void operator=(Poly& const) 意味着您的类型将不再满足 CopyAssignable concept,这需要 T& return 类型。

不满足 CopyAssignable 概念的类型不能正式用于某些 standard-container 操作,例如 std::vector::insert,这意味着以下看似无辜的代码产生未定义的行为,即使它可能 运行 完全没问题:

#include <vector>

struct Poly
{
    void operator=(Poly const&) {} // Poly is not CopyAssignable
};

int main()
{
  std::vector<Poly> v;
  Poly p;
  v.insert(v.begin(), p); // undefined behaviour
}

正如 C++ 标准在 § 17.6.4.8/2.3 中解释的那样,它讨论了对使用标准库的程序的约束:

(...) the effects are undefined in the following cases:

(...) for types used as template arguments when instantiating a template component, if the operations on the type do not implement the semantics of the applicable Requirements subclause (...).

当然,恰恰是因为未定义的行为允许编译器忽略错误并使程序表现得很好,与明显预期的行为相匹配。但这不是必需的。

您还应该考虑到您无法预测 Poly 类型的所有未来用途。有人可能会写一些模板函数,例如:

template <class T>
void f(T const& t)
{
    T t2;
    T t3 = t2 = t;
    // ...
}

此功能将不适用于您的 Poly class。

只要不违反此 C++ 约定,您就不会 运行 惹上麻烦。

what happens when you return *this?

在您的示例 (p2 = p1;) 中,什么也没有。该方法将 p1 复制到 p2 和 returns 对 'this' 对象的引用,调用代码不使用该对象。

p3 = p2 = p1; 等代码中,第一个调用是 p2 = p1,它将 p1 复制到 p2 和 returns 对 [=12] 的引用=].然后调用代码从 reference-to-p2 复制到 p3(并忽略返回的对 p3 的引用)。

(顺便说一下:您的单元测试是否确保 p1 = p1 正常工作?很容易忘记这种情况!)