c++ 中的函数查找
template <class T> class visibility { public: void say(double d){}; private: void say(int i){}; void say(T t){}; }; int _tmain(int argc, _TCHAR* argv[]) { visibility<char*> v; v.say(123); // error C2248: 'visibility<T>::say' : cannot access private member declared in class 'visibility<T>' // 重载决议后: void say(int i){}; v.say("123"); // error C2248: 'visibility<T>::say' : cannot access private member declared in class 'visibility<T>' // 重载决议后: void say(T t){}; return 0; }
0 访问限制:
1 private: 它的名字只能被所声明的类的成员和友元使用。 2 protected: 它的名字可以被所声明的类的成员和友元使用,也可以被派生类的成员和友元使用 3 public: 它的名字可以无限制的用在任何地方。
1 发生在调用之前
cpp文件中调用的一个函数(或成员函数),编译器主要做了下面四件事情:
1).Name lookup(名字查找)
确定一系列的候选者。(注意所谓 '名字查找' 中的 '名字' 仅仅是函数名, 而不是函数签名, 即不包括返回值, 参数)
(1) 范围查找, 范围一旦确定下来, 就停止范围查找.
a) 如果有限定名 (X::func), 则在X::中查找; 找不到就 error;
b) 如果是成员函数(object->func), 则在类中查找, 找不到,再去基类中查找; 找不到就 error;
c) 如果形如 `func()` 的函数, 查找规则如下:
<c-1> 外层函数(调用者)所在类中查找; 找不到就到 <c-2>;
<c-2> 基类中查找, 找不到再去外层基类中查找(baseclass -> 外层的 baseclass -> ...); 一旦找到就停止查找; 找不到就到 <c-3>;
eg: void A::say() { func(); } 去say 所在的类A中找, 找不到再去A的基类中找。
<c-3> 在当前的namespace 中查找; 找不到再去外层 namespace 中查找(当前的 namespace-> 外层的 namespace -> 更外层的 namespace....) ; 一旦找到就停止查找; 找不到就 error;
注意: 如果函数参数是 class/struct, 那么由于koening 查询, 参数类型所在的 namespace 也会被列为查找范围。所以参数类型所在的 namespace 中的名字也会被列入到候选范围。
eg: 这里在 namespace Lv2 中找到了 'func', 通过 koening 在 koeningNs 中也找到了 'func', 所以 (III) 查找到的候选者有: { (I) koeningNs::func , (II) Lv3::Lv2::func }
namespace koeningNs { class KCls {}; void func(KCls){} // (I) } namespace Lv3
{ namespace Lv2
{ void func(koeningNs::KCls){ } // (II) namespace Lv1 { class myCls { public : void say() { func(koeningNs::KCls()); // (III) // error C2668: 'Lv3::Lv2::func' : ambiguous call to overloaded function } }; } } } void test_nn() { Lv3::Lv2::Lv1::myCls obj; obj.say(); };
<c-4> 最后确定候选者们 : { 类中的候选者们 } or { 基类中的候选者们 } or { namespace 中的候选者们}
注意 : 一旦编译器确定了一个候选者集合, 就停止查找。比如 : 找到了 { 类中的候选者们 }, 那么就不会再关注 { 基类中的候选者们 } 更会不关注 { namespace 中的候选者们}。
(2) 在找到的范围中, 确定一系列候选者
比如: 在class 中找到了say, 那么可以确定候选者:
void say(double d);
void say(int i);
2.Overload resolution(重载决议)
编译器开始执行重载决议, 根据参数最匹配原则,从候选者中选出最匹配的函数。如不唯一,就存在二义性。
3.Accessibility checking(可访问性检测).
4. 如果选中的是模板函数, 那么还要进行模版特化决议
class CTemp { public: template <typename T> void sayHello() { cout << " sayHello : 主模板" << endl; } private: template <> void sayHello<int>() { cout << "sayHello : 特化模板" << endl; } private: template <typename T> void sayBonjour() { cout << " sayBonjour : 主模板" << endl; } public: template <> void sayBonjour<int>() { cout << " sayBonjour : 特化板" << endl; } }; void test_nn() { CTemp ct; ct.sayHello<int>(); // 主模板 public, 特化模板 private // 可以调用特化的 private. ct.sayBonjour<int>(); // error C2248: 'CTemp::sayBonjour' : cannot access private member declared in class 'CTemp' // 主模板 private, 特化模板 public // 主模板在 '可访问性检查' 的时候就被认为 '不可访问', 即使特化模板 public 也不行。 };