为何using directive隐式声明实体后名称查找未终止?
为什么这段C++代码输出666而非2?
先看这段代码:
#include<iostream> using namespace std; namespace B { int ohoh=2; } namespace A { int ohoh=666; namespace C { //using B::ohoh;(as if declared by using directive) //why does the lookup not stops here? int foo() { using namespace B; cout<<ohoh<<endl; } } } int main() { A::C::foo(); }
运行后输出的是666,但很多人会直觉认为应该输出B::ohoh的2,这背后的关键是搞懂C++的非限定名称查找规则和using namespace指令的作用方式。
咱们先明确核心规则:
对于非限定名称(即没写
::前缀的名称),名称查找会从最内层作用域开始往外找,只要找到任意一个声明就立刻终止,不会继续检查外层作用域。
而using namespace指令的作用是:把被引入命名空间的所有声明,“注入”到同时直接或间接包含该using指令和被引入命名空间的最近外层命名空间中。
现在来拆解你的代码里的查找过程:
- 当在
A::C::foo()里写using namespace B;时,这个指令的“最近外层命名空间”是全局命名空间——因为foo()在A::C里,而B是直接定义在全局下的,同时包含foo()所在的嵌套作用链和B的最近外层就是全局空间。所以B::ohoh会被注入到全局作用域,而不是A::C或者A的作用域里。 - 当
foo()里引用ohoh时,非限定名称查找的顺序是:- 首先查
foo()的局部作用域:没有ohoh的声明; - 然后查外层的
A::C作用域:也没有ohoh的声明(注释掉的代码不算); - 接下来查
A的作用域:这里找到了int ohoh=666;的声明,所以查找直接终止,根本不会走到全局作用域去看被注入的B::ohoh。
- 首先查
这就是为什么最终输出的是A::ohoh的666,而不是B::ohoh的2。
如果想要让foo()里的ohoh指向B::ohoh,有几种更可靠的方式:
- 直接使用限定名称:
cout << B::ohoh << endl;,完全避免名称查找的歧义; - 在
foo()里用using B::ohoh;代替using namespace B;,这样会把B::ohoh直接引入到foo()的局部作用域,查找时会优先找到它; - 如果一定要用
using namespace,可以把它放到A::C的作用域里,但此时还是会先找到A::ohoh,所以这种情况下不如直接用using B::ohoh;更明确。
最后非常认同你的观点:尽可能少用using directive(也就是using namespace X;),它很容易引入命名冲突,或者像这个例子一样导致意想不到的名称查找结果。相比之下,using声明(比如using X::y;)更精确,能大幅减少这类问题的发生。
内容的提问来源于stack exchange,提问作者choxsword
相关产品推荐
相关产品推荐

