函数的静态重载只针对在同一个作用域中具有相同名称的函数才有效,不同作用域中的函数不行。在函数调用处,编译器会将同一作用域中具有相同名称的函数都作为候选函数,一旦找到函数名称,就不再从其他作用域中查找函数名称了。找到名称后,编译器才进行类型检查,查看调用处的函数与上面声明处的是否有一致的函数。
重要的是:名称查找优先于类型检查,举个例子:
int f1() {return 1;}
namespace
{
int f1(int a) {return a + 1;}
void f1() {}
// 调用f1
int s = f1();
}
上面定义了三个同名函数,一个定义在外层的全局作用域里面,两个定义在内部的匿名作用域里面。如果编译此段代码,编译器会报错,说调用的函数缺少参数,原因是:
- 编译器在调用f1的地方发现一个名称
f1
,因此,编译器就会去寻找名称f1
的声明,其会优先在内层作用域(即此处的匿名作用域里)里查找名称f1
,如果没有找到,则继续往外层查找,去到全局作用域里面找f1
; - 编译器在内层作用域里面找到了
f1
的声明,且找到了两个,此时查找停止,编译器不再继续往外层查找(只要找到名称,就不会再继续往上层作用域里查找了,不论在内层找到了几个),编译器会把找到的这两个名称加入到候选匹配列表中; - 名字查找完成后,就进行类型检查。编译器发现调用处的
f1
没有形参,且有返回值,而找到的两个f1
的声明都不符合要求,此时编译器报错。
由上面可看出,函数静态重载只适用于同一个作用域中的同名函数,不同作用域中的同名函数不发生静态重载。实际上,内层作用域中的名称会隐藏其外层作用域中的相同的名称,而这个隐藏的原理应该就是上面3点的分析过程。
要记住的是:名称查找永远优先于类型检查,这个在类的继承体系中也是如此,比如继承体系中的非虚函数的调用:
class Base
{
public:
int f1(int a) {return a + 1;}
};
class Driv : public Base
{
public:
int f1() {return 2;}
};
int main()
{
Driv d;
int s = d.f1(); // 调用正确
int s1 = d.f1(3); // 调用出错,编译器报出多加了形参
return 0;
}
上述在main函数中调用f1
时,也进行了上面所描述的3步。显然,在第一处调用的地方是正确的,因为编译器先找到了内层作用域(即类Driv的作用域)中的名称f1
,此时,编译器不再继续往其父类作用域查找,因此编译器只找到了没有形参的f1
,而有形参的f1
并没有找。