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

来源:互联网 发布:app store 软件 发票 编辑:程序博客网 时间:2024/04/30 22:25

  出自于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];    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类的加号操作符重载函数是有两个版本的。如下正常运行的代码:

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

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

string& operator+(const string& s);

和友缘全局函数的

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

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

s4 = s2.operator+(s1);

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

阅读全文
1 0