1、有时看上去好像一个成员操作符函数重载了一个非成员的操作符,其实并非如此。
这不是重载,只是不同的查找算法。
考虑如下类,它以成员函数的形式重载了一个操作符函数:
class X {
public:
X operator %( const X & ) const; //二元取余操作符
X memFunc1( const X & );
void memFunc2( );
//....
};
可以采用中缀或函数调用语法来调用这个重载的操作符函数:
X a, b, c;
a = b % c; //采用中缀语法调用成员操作符%
a = b.operator % ( c) ; //成员函数调用语法
a = b.memFunc1( c); //另一个成员函数调用
当我们使用函数调用语法时,应用的是普通的查找规则,也就是说,对b.operator %(c)调用的处理方式与memFunc1的相同。
然而,对重载操作符的中缀调用的处理机制则与此不同:
X operator %( const X &, int ); //非成员操作符
//.....
void X::memFunc2( ) {
*this % 12; //调用非成员操作符%
operaotr %( *this, 12 ); //错误,实参太多
}
对于中缀操作符调用来说,编译器不仅会考虑成员操作符,也会考虑非成员操作符,
因此第一个对operator%的中缀调用,将会匹配非成员的那一个。
不是一个重载的例子,而是编译器在两个不同的地方查找候选函数。
第二个对operator%的非中缀调用遵循标准的函数查找规则,因而匹配那个成员函数。
这里我们遇到一个错误,因为我们试图将三个实参传递给一个二元函数(记住,对成员函数的调用存在一个隐式的实参this)。
实际上,对重载操作符的中缀调用执行了一个退化形式的ADL,即当确定将哪些函数纳入重载解析考虑范围时,中缀操作符中左参数的类(可能只有一个左参数,而没有右参数)的作用域和全局作用域都被考虑在内。
ADL则将这个过程扩展到被操作符实参所带入的其他名字空间中候选操作符函数。
注意这并不是重载。
重载是一个和函数声明有关的静态属性,而ADL和中缀操作符函数查找都属于提供给函数调用的实参的属性。