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
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
void g()
{}
void h()
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
g( 1 );
}
}
这里的g(1)在1.(5)这里找到了符号g,就不会向上到1.(6)查找到真正的g(int)了
例3
class Y
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
void f( int )
{} // [1]
public :
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
void f( double )
{} // [2]
} ;
![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
int main()
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
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()
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
int * p = 0 ;
f(p);
}
这里的f(p)会调用[3]吗?
不会,因为在进行到第二步重载决议的时候,只有[1]和[2]参与了重载决议,结果选择了
[2],那么[1]的特化版本[3]当然就轮不到了。
例5
class X
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
template < typename T > void g()
{}
public :
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
template <> void g < int > ()
{}
} ;
![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
int main()
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
X y;
y.g < int > ();
}
这里首先第3步访问检查发现g为private(此时g的特化版本被直接无视了),所以即使
g<int>为public, 该段代码仍然不能够编译通过
例6
namespace E
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
class X
{} ;
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
void f(X)
{} // [1]
}
void f(E::X)
{} // [2]
class X
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
public :
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
void f()
{} // [3]
void g()
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
E::X x;
f(x); // [4]
}
} ;
![](http://www.cppblog.com/Images/OutliningIndicators/None.gif)
[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)
![](http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif)
{
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)根本就没有机会被编译器访问到
posted on 2006-12-27 11:04
shifan3 阅读(2083)
评论(8) 编辑 收藏 引用 所属分类:
C++