一个例子了解operator+

2 minute read

Published:

先来看一个题目:

#include <iostream>
using namespace std;

class A {
	int a;
public:
	A(int x=0): a(x){}
	void f(){cout << a;}
	A operator+(int op) {
		A T;
		T.a = a + op;
		return T;
	}
};



int main()
{
   A a1, a2(2);
   cout << a1 + a2;  // A
   (a1 + a2).f();	 // B
   (a1 + 3).f();	 // C
   (3 + a1).f();	 // D
   return 0;
}

以上 main 函数中 A,B,C,D 四个选项能通过编译?

考验对 C++ 中运算符重载的理解的时候到了!先给出答案:

cout << a1 + a2;	// A  --> failed
(a1 + a2).f();		// B  --> failed
(a1 + 3).f();		// C  --> passed
(3 + a1).f();		// D  --> failed

为啥会是这样呢?看一个稍复杂些的例子(把各种情况都列一下):

#include <iostream>
using namespace std;

class A {
	int a;
public:
	A(int x=0): a(x){}
	void f(){cout << "operator+(int): " << a << endl;}
	A operator+(A op) {
        A T;
        T.a = a + op.a;
        return T;
    }
};

class B {
    int b;
public:
    B(int x=0): b(x) {}
    void f() {cout << "operator+(Obj): " << b << endl;}
    B operator+(int op) {
        B T;
        T.b = b + op;

        return T;
    }
};


int main()
{
    A a1, a2(2);
    B b1, b2(2);
    // operator+ 的参数为 A 时,int 型 op 作为第二操作数
    (a1 + 3).f(); // pass
    // operator+ 的参数为 A 时,int 型 op 作为第一操作数
    (3 + a1).f();  // failed
    // operator+ 的参数为 A 时,A 型 op 作为操作数
    (a1 + a2).f();  // pass

    // operator+ 的参数为 int 时,int 型 op 作为第二操作数
    (b1 + 3).f();  // pass
    // operator+ 的参数为 int 时,int 型 op 作为第一操作数
    (3 + b1).f();  // failed
    // operator+ 的参数为 int 时,B 型 op 作为操作数
    (b1 + b2).f(); // failed

    return 0;
}

以上各个例子,可以发现一个规律:加法操作操作符有两个操作数,能否能够编译取决于对象是第几个操作数以及重载 operator+ 时的参数类型,且成员函数默认把 this 指针当作第一个参数传进去了。例如,(a1 + 3).f()pass 是因为定义了 operator+(A),等价于 operator+(A, A),因为 3 可以通过构造函数转化为 A(3) 所以执行时相当于:第一个操作数是 a1,所以调用的是 A 重载的加法运算符,然后用 3 实例化为一个 A 类型的对象,再传给 operator+。所以,能否编译通过,我们要看第一个操作数的类型来确定,再根据第二个操作数来确定参数的类型,如果第一个操作数的类型中定义了该签名的 operator+ 则可以编译通过,否则 failed。所以:

 // operator+ 的参数为 A 时,int 型 op 作为第二操作数
 (a1 + 3).f(); // pass。目标:operator+(A, int),存在:operator(A*, A),3 可以实例化为 A,所以 passed
 // operator+ 的参数为 A 时,int 型 op 作为第一操作数
 (3 + a1).f();  // failed。目标:operator+(int, A),存在:operator(A*, A),不匹配,如果 A 中引入友元函数 operator(int, A) 则可以 passed
 // operator+ 的参数为 A 时,A 型 op 作为操作数
 (a1 + a2).f();  // pass。显然!

 // operator+ 的参数为 int 时,int 型 op 作为第二操作数
 (b1 + 3).f();  // pass。目标:operator+(B, int),存在:operator+(B*, int)
 // operator+ 的参数为 int 时,int 型 op 作为第一操作数
 (3 + b1).f();  // failed。目标:operator+(int, B),存在:operator+(B*, int),不匹配,如果 B 中引入友元函数 operator+(int, B) 则 passed
 // operator+ 的参数为 int 时,B 型 op 作为操作数
 (b1 + b2).f(); // failed。目标:operator+(B, B),存在:operator+(B*, int),不匹配

引入友元函数的例子:

class A {
	int a;
public:
	A(int x=0): a(x){}
	void f(){cout << "operator+(int): " << a << endl;}
	A operator+(A op) {  // 其实等价于:operator+(A* , A),this 指针作为第一个参数
        A T;
        T.a = a + op.a;
        return T;
    }
    friend A operator+(int op1, A op2) {  // 友元函数
        A T;
        T.a = op1 + op2.a;

        return T;
    }
};

这种情况下 (3 + a1).f() 即可 passed

结论:根据操作数的类型和位置确定需要的 operator+ 的具体函数签名,如果在名字空间中能找到定义的函数则可以,否则 failed!