当代码调用某类的成员函数时,下面三件事依次发生:
首先,编译器查找这个函数名字;编译器查找名为指定名字的函数时,并不在意访问权限和参数类型是否匹配,它只是在“最接近”的那个范围内查找所有名字能匹配的函数。仅当函数在“最接近”的那个范围内没找到任何一个名字能与该调用匹配的函数,才会“向外”退一层,在更广的范围内重复上述查找(比如从派生类“退”到基类,或者从某名字空间“退”到全局空间)。如果退到无路可退还是没有找到指定名字的函数,这就是个编译错误。如果在任何一层找到了至少一个名字能匹配的函数,查找过程停止(注意,更“外层”即使还有更多指定名字的函数定义,编译器也不会考虑它们了)。
第二,重载裁决。编译器从所有符合条件的候选函数中,按照参数类型匹配关系,选择最佳匹配;
第三,编译器检查调用代码是否有权访问最佳匹配的函数。
函数调用到底对应到哪一个函数体,我们经常发生误解。误解的根源主要不是在那些复杂的名字查找和重载裁决规则,误解的根源主要在于对上述三个依次进行的步骤不清楚。
看例子:
#include
void f(double) { }
class B {
public:
void f(double) { }
};
class D: public B {
void f(double) { }
public:
void f(int) { }
};
int
main() {
D d;
d.f(12.3);
}
编译得到的错误信息:
main.cpp: In function `int main()':
main.cpp:13: error: `void D::f(double)' is private
main.cpp:21: error: within this context
编译过程到底发生了什么?为什么得到那些错误信息?
第一步:编译器查找函数名字f。因为我们在调用D的实例上的方法,编译器从D的范围开始查找,马上找到D::f(int)和D::f(double)。关键来了:查找过程到此停止!对于更“外层”(此处即基类B,以及全局域)中定义的f,编译器不会考虑在内了!
第二步:选择最佳匹配。这里有两个候选者,那么它们能匹配吗?D::f(double)当然是直接的最佳匹配。
第三步:检查访问权限。D::f(double)是私有成员,编译出错。注意:这里虽然D::f(int)通过一个合法的隐式转换也能匹配(这个隐式转换如果发生,会有编译警告),而且访问权限也没问题,但这一切都没用了!因为上一步的重载函数裁决中不考虑访问权限!一旦在第二步中确定具体要调用哪一个实现,其他被舍弃的重载版本就都不会再被考虑了!
所以出现了上述这个看起来十分“古怪”的例子:三个在不同位置定义的f(double),居然没有一个能让d.f(12.3)这个调用正常完成,而引起了编译错误。
本文内容来自《Exceptional C++》和《C++ Common Knowledge》相关章节
阅读(778) | 评论(0) | 转发(0) |