C++的名称查找顺序

解析C++符号查找与函数重载机制

1。符号查找(对于函数此时只看名字,不看参数)

    大致顺序是

    (1)如果有限定名( XXX:: )那么就直接在XXX里查找

    (2)函数局部名字空间

    (3)(如果是成员)类名字空间

    (4)递归向上至所有基类的名字空间

    (5)当前名字空间

    (6)递归向外至所有外层名字空间,

    在任一层里, 用using导入的符号和该层的其他符号同一对待。

    keonig查找: 对于函数, 如果参数为类/结构/模版类并位于其他的名字空间,
    在(5)和(6)的查找中导入该空间(不递归向外)的符号一同查找.

2。(如果是函数)重载决议(注意此时特化的函数不参与决议)

3。(如果是类内部的名字)检查访问权(注意此时特化的函数仍然不参与决议)

4。(如果找到了一个模版)模版特化决议

 

编译器执行以上步骤的时候是使用贪心匹配,只要找到一个符合当前检查内容的就会停止查

所以任何一层都有可能发生错误的掩盖情况

例1

void  f( int ) {} 
  class  Y
  {
  public :
       void  f() {} 
     Y()
       {
         f( 1 );
     } 
 } ;



这里的f(2)在1.(2)这里找到了符号f,就不会向上到1.(5)查找到真正的f(int)了

例2

void  g( int ) {} 
 namespace  S
 {
     void  g() {} 
 
     void  h()
     {
        g( 1 );
    } 
} 

这里的g(1)在1.(5)这里找到了符号g,就不会向上到1.(6)查找到真正的g(int)了

例3

 class  Y
 {
     void  f( int ) {}   // [1] 
 public :
     void  f( double ) {}   // [2] 
 } ;

 int  main()
 {
    Y y;
    y.f( 1 );
} 


y.f(1)会调用[2]吗?不会,因为在第2步重载决议的时候就选定[1]了,因此这段代码会报
出无法访问private成员的错误

例4

template  < typename T > 
 void  f(T) {}   // [1] 
 
template  < typename T > 
 void  f(T * ) {}   // [2] 
 
template  <> 
 void  f < int *> ( int * ) {}   // [3] 
 
 
 int  main()
 {
     int   * p  =   0 ;
    f(p);
} 


这里的f(p)会调用[3]吗?
不会,因为在进行到第二步重载决议的时候,只有[1]和[2]参与了重载决议,结果选择了
[2],那么[1]的特化版本[3]当然就轮不到了。

例5

class  X
 {
    template  < typename T >   void  g()  {} 
 public :
    template  <>   void  g < int > () {} 
} ;

 int  main()
 {
    X y;
    y.g < int > ();
} 


这里首先第3步访问检查发现g为private(此时g的特化版本被直接无视了),所以即使
g<int>为public, 该段代码仍然不能够编译通过

例6

 namespace  E
 {
     class  X {} ;
     void  f(X) {}   // [1] 
 } 
 
 void  f(E::X) {}    // [2] 
 
 class  X
 {
 public :
     void  f() {}   // [3] 
      void  g()
     {
        E::X x;
        f(x);  // [4] 
     } 
} ;


[4]会调用那个呢? 在1.(3)里就确定了是[3],因此参数不匹配
如果注释掉[3]的f,那么由于koenig查找, 在1.(5)里[1]和[2]都会是平等的可选项
所以会出现二义性.
如果把namespace E改为class E, 把E中的f改为静态函数
由于koenig查找仅仅导入参数的名字空间, 因此[1]将不参与1.(5)的查找,
最终结果会是[2]

 

例7
这是一个现实中的例子,如果想给std::pair写一个ostream的输出函数,应该如何实现呢?

template<class U, class V>   
ostream& operator<<(ostream& s, const pair<U, V>& p)   
{   
    return s << p.first << " : " << p.second;   
}


差不多该这样吧

但是如下代码就会导致出错

map<string, string> a;     
copy(m.begin(), m.end(), ostream_iterator<pair<string, string> >(s, "\n"));


为什么呢?
因为在ostream_iterator的实现代码里调用了pair的operator<<
由于ostream_iterator的代码是在std名字空间里的,因此编译器会首先在std里查找是否存在operator<<
一旦找到(尽管找到的operator<<不是给pair使用的),就不会继续到全局名字空间里查找我们自己定义的operator<<了

因此解决方案是把自定义的operator<<放到std名字空间里

namespace std
{
    template<class U, class V>   
    ostream& operator<<(ostream& s, const pair<U, V>& p)   
    {   
        return s << p.first << " : " << p.second;   
    }   
}


就可以了

例8

传说中的hide

struct A
{
   void f(int){}
};
struct B: public A 
{
   void f(void){}
};

int main()
{
   B b;
   b.f(1);
}



b.f(1)能编译通过吗?不能,因为编译器会首先在B的名字空间里查找f,找到了void f(void),然后就会停止查找
因此A里的f(int)根本就没有机会被编译器访问到
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值