为什么在类之外定义operator +或+ =,以及如何正确地执行它?

Dav*_*ave 27 c++ operator-overloading operators

我对它们之间的差异感到有点困惑

Type  operator +  (const Type &type);
Type &operator += (const Type &type);
Run Code Online (Sandbox Code Playgroud)

friend Type  operator +  (const Type &type1, const Type &type2);
friend Type &operator += (const Type &type1, const Type &type2);
Run Code Online (Sandbox Code Playgroud)

哪种方式是首选,它们看起来像什么,什么时候应该使用?

Jon*_*Jon 29

运算符的第一种形式是您在类中定义的内容Type.

第二种形式的运算符是您在与类相同的命名空间中定义为独立函数的形式Type.

定义独立函数是一个非常好的主意,因为那些操作数可以参与隐式转换.

假设这个类:

class Type {
    public:
    Type(int foo) { }

    // Added the const qualifier as an update: see end of answer
    Type operator + (const Type& type) const { return *this; }
};
Run Code Online (Sandbox Code Playgroud)

然后你可以写:

Type a = Type(1) + Type(2); // OK
Type b = Type(1) + 2; // Also OK: conversion of int(2) to Type
Run Code Online (Sandbox Code Playgroud)

但你不能写:

Type c = 1 + Type(2); // DOES NOT COMPILE
Run Code Online (Sandbox Code Playgroud)

operator+一个免费的功能可以让最后一种情况下也是如此.

然而,运算符的第二种形式错误的是它通过直接调整其操作数的私有成员来执行添加(我假设,否则它不需要是朋友).它应该这样做:相反,运算符也应该在类中定义,并且独立函数应该调用它们.

为了了解这将如何发展,让我们要求大师的服务:http://www.gotw.ca/gotw/004.htm.滚动到最后,看看如何实现独立功能.

更新:

正如James McNellis在他的评论中所说的那样,给出的两种形式也有另一个不同之处:左手边在第一个版本中不是const限定的.由于操作数operator+实际上不应该作为添加的一部分进行修改,所以始终对它们进行常量限定是一个非常好的主意.Type我的示例中的类现在执行此操作,最初它没有.

结论

应对运营商的最佳方式+,并+=为:

  1. 定义operator+=T& T::operator+=(const T&);您的类中.这是实施添加的地方.
  2. 定义operator+T T::operator+(const T&) const;您的类中.该运算符将根据前一个运算符实现.
  3. T operator+(const T&, const T&);在类外部提供自由函数,但在同一名称空间内.该函数将调用该成员operator+来完成工作.

您可以省略步骤2并T::operator+=直接进行自由函数调用,但作为个人喜好,我希望将所有的加法逻辑保留在类中.

  • @Jon:最好的方法:`T&T :: operator + =(T const&);`和`T operator +(T lhs,T const&rhs){return lhs + = rhs; } - >你的第2步是假的,你的签名在第3步中关闭了.(编译器可以更好地优化函数签名中的副本,特别是对于即将发生的移动语义) (2认同)

GMa*_*ckG 5

对于C++ 03和C++ 0x(NRVO和move-semantics),实现运算符的正确方法是:

struct foo
{
    // mutates left-operand => member-function
    foo& operator+=(const foo& other)
    {
        x += other.x;

        return *this;
    }

    int x;
};

// non-mutating => non-member function
foo operator+(foo first, // parameter as value, move-construct (or elide)
                const foo& second) 
{
    first += second; // implement in terms of mutating operator

    return first; // NRVO (or move-construct)
}
Run Code Online (Sandbox Code Playgroud)

请注意,将上述内容合并为:

foo operator+(foo first, const foo& second) 
{
    return first += second;
}
Run Code Online (Sandbox Code Playgroud)

但有时(在我的测试中)编译器不启用NRVO(或移动语义),因为它不能确定(直到它内联变异运算符)与之first += second相同first.更简单,更安全的是拆分它.