随笔 - 31  文章 - 128  trackbacks - 0
<2007年1月>
31123456
78910111213
14151617181920
21222324252627
28293031123
45678910

常用链接

留言簿(5)

随笔分类(38)

随笔档案(31)

收藏夹(4)

College

High School

最新随笔

搜索

  •  

积分与排名

  • 积分 - 54886
  • 排名 - 411

最新评论

  • 1. re: [yc]详解link
  • 面试的时候面试官就问过我什么是编译和链接,我说编译就是把代码文件生成目标文件,链接就是把目标文件生成可执行文件,他说不对,又问我什么是动态链接,还问我预编译都做什么处理。。。都在这里找到了答案!!!!
  • --王至乾
  • 2. re: [yc]详解link
  • @刘伟
    我是说博主,不是叫你啊
  • --溪流
  • 3. re: [yc]详解link
  • 谁是石老师,我不是哈@溪流
  • --刘伟
  • 4. re: [yc]详解link
  • 石老师?我是溪流~
  • --溪流
  • 5. re: [yc]详解link
  • 期待楼主下文啊,多谢楼主了
  • --刘伟

阅读排行榜

评论排行榜

 

1。符号查找(对于函数此时只看名字,不看参数)
    大致顺序是
    (1)如果有限定名( XXX:: )那么就直接在XXX里查找
    (2)函数局部名字空间
    (3)(如果是成员)类名字空间
    (4)递归向上至所有基类的名字空间
    (5)当前名字空间
    (6)递归向外至所有外层名字空间,
    在任一层里, 用using导入的符号和该层的其他符号同一对待。
    keonig查找: 对于函数, 如果参数为类/结构/模版类并位于其他的名字空间,
    在(5)和(6)的查找中导入该空间(不递归向外)的符号一同查找.

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

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

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

 

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

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

例1

 1 void  f( int ) {}
 2 class  Y
 3 {
 4 public :
 5      void  f() {}
 6     Y()
 7      {
 8         f( 1 );
 9     }

10 }
;


这里的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   * =   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<stringstring> a;     
copy(m.begin(), m.end(), ostream_iterator
<pair<stringstring> >(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)根本就没有机会被编译器访问到
posted on 2006-12-27 11:04 shifan3 阅读(2093) 评论(8)  编辑 收藏 引用 所属分类: C++

FeedBack:
# re: 总结一下C++的名称查找顺序 2007-01-03 16:19 大熊猫
对特化不大了解,不知道特化用在什么地方,看起来只要有模板函数,编译器就无视特化函数,能举个编译器不无视特化函数的例子吗?  回复  更多评论
  
# re: 总结一下C++的名称查找顺序 2007-01-04 14:10 Francis Arcanum
@大熊猫
如果没有重载的函数,或者重载决议决定使用有特化版本的函数,那么该函数特化版本就不会被无视了。

例如
template <typename T>
void f(T){} //1

template <>
void f<int>(int){} //2
这里没有重载,所以直接考虑特化
或者
template <typename T>
void f(T){} //1

template <typename T>
void f(T*){} //2,对1重载

template <>
void f<int>(int*){} //3, 对2特化

调用
int *p = 0;
f(p);
这里重载决议选择了2,那么2的特化版本3就会被纳入考虑范围了

  回复  更多评论
  
# re: 总结一下C++的名称查找顺序 2007-01-04 19:26 大熊猫
是不是在没有重载的情况下,编译器找到一个符合内容的就会符号就会地址搜索,所以例5中的那个特化没有被考虑呢?  回复  更多评论
  
# re: 总结一下C++的名称查找顺序 2007-01-05 02:06 Francis Arcanum
@大熊猫
例5是因为判断访问权是在处理特化之前
所以还没开始找特化版本编译器就发现了错误,不会继续下去了

另外对于符号搜索,针对第一步
其中每一个小步如果找到符合的符号,都会直接结束符号搜索

而2、3、4等步是在找到符号的那一层里进行操作  回复  更多评论
  
# re: 总结一下C++的名称查找顺序 2007-01-05 13:02 大熊猫
谢谢您
我还是弄大不懂
你的意思说编译器看到y.g<int>(),查找到那个模板函数以后就不继续找了,因为它是私有的,就直接报错了(尽管还有一个公有的特化版本)。那如果不是私有的,那么它就准备实例化,当实例化的时候它还会去查符号?然后发现一个特化的版本,然后又调用了特化版本的?  回复  更多评论
  
# re: 总结一下C++的名称查找顺序 2007-01-05 13:16 大熊猫
void f(T) {} // [1]

template < typename T >
void f(T * ) {} // [2]

template <>
void f < int *> ( int * ) {} // [3]
void f(int *)
{
cout<<"111"; //[4]
}
int _tmain(int argc, _TCHAR* argv[])
{

int * p = 0 ;
f(p);
int pause;
cin>>pause;

return 0;
}
我又加了一个函数,我想问一下,在这个小程序里,编译到底有没有对二个模板函数做重决呢?难道编译器先对二个模板函数进行重载的决议,然后决定哪个模板更好一些,然后再在实例化的时候,在比较匹配的模板所有可能的实例化版本和它的特化版本,还有函数4,中选择一个最佳。不好意思,不知道有没有表达清楚我的意思。  回复  更多评论
  
# re: 总结一下C++的名称查找顺序 2007-01-05 13:31 大熊猫
@Francis Arcanum
有点明白了,如果出现像上述小程序中的4函数,因该一开始编译器就决定用它。如果没有的话,再在二个模板函数中进行判断,决定使用哪一个。然后发现是个模板函数,要实例化了,看见一个很匹配的特化版本,就直接用它了,对吗? 上课要迟到了,哈哈,谢谢兄弟~。  回复  更多评论
  
# re: 总结一下C++的名称查找顺序 2007-01-05 16:00 Francis Arcanum
@大熊猫
这里1,2,4进行重载,则直接选择了4
3在重载决议的时候不考虑。
如果重载决议选择了1,那么就在1和3里面选择

在重载决议的时候,1,2,4是平等的。但是4最匹配。
如果理解为先考虑非模板函数4,再考虑模板函数1、2,其实效果也完全一样。  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理