为什么有的操作符重载函数只能是成员函数?

看c++面向对象高级开发的课时,操作符重载的问题。


出自于c++ primer 5e的一句话:

赋值(=)、下标([])、调用(())和成员访问箭头(->)运算符必须是成员(函数)。

赋值运算符。

我们知道一个c++类,程序员如果没有为其定义了赋值操作符重载函数,编译器也会隐式的定义,这样倘若再定义全局赋值运算符重载函数,将会发生二义性。即使编译器允许这样的定义手法,在调用的时候也编译不过:

cls& operator=(cls& c1, const cls& c2)
{
    //...
    return c1;
}

class cls
{
public:
    int m_a;
    char m_c;

    cls(int a, char c) : m_a(a), m_c(c) {}
};

int main(void)
{
    cls c1(1, 2), c2(3, 4);

    c1 = c2;    //调用的是编译器隐式定义的operator=()还是程序员显示定义的全局的operator=()

    return 0;
}

  操作符重载函数规定是类的成员函数,有一个至关重要的特点:类的this指针会被绑定到运算符的左侧运算对象,成员运算符函数的显示参数比运算符对象总数少一个。也就是说上文提到这些运算符的左操作数必须是该类类型的参数,换句话说,假设c++编译器允许[]操作符重载函数是全局的,那么程序员完全可以写出:

cls& operator[](int dat, cls& c)
{
    //...
    return c;
}

int main(void)
{
    cls c(1, 'h');
    6[c];//c++编译器允许[]操作符重载函数是全局的

    return 0;
}

  因为[]操作符重载函数是全局(友元)的,也就是没有了该函数的左操作数是this指针的限制,程序员可以任意定义左操作数的类型,类似的,就会出现6=c, 6(c), 6->c的代码,显然这样的代码是错误的。 
  不仅如此,假设cls类定义了转换构造函数

cls::cls(int i);

  那么 operator的函数原型大可以是:

cls& operator[](cls& c1, cls& c2)
{
    //...
    return c1;
}

  看似正确,但是若用户利用转换构造函数的隐式类型转换调用该函数:

int main(void)
{
    cls c(1, 'h');
    6[c];

    return 0;
}

  同样编译通过,显然这还是错误的。因此,c++编译器对这些操作直接视为语法问题。


string类的加号操作符重载函数

另外我们可以推测string类的加号操作符重载函数是有两个版本的。如下正常运行的代码:

std::string s1 = "hello";
const char* s2 = "world";

string s3 = s1 + "world";   
string s4 = s2 + s1;

  代码中的两个加法操作分别执行成员函数

string& operator+(const string& s);

    和友元全局函数

string& opreator+(std::string s1, std::string& s2);

因为假设二者都是调用成员函数的operator+(),那么第2个加法语句等价于

s4 = s2.operator+(s1);

然而s2是const char*类型,根本没有成员函数

posted @ 2018-08-06 17:36  lightmare  阅读(1456)  评论(0编辑  收藏  举报