为什么我在使用命名空间foo时不能在sub :: bar中使用foo :: bar函数?

ein*_*ica 4 c++ scope namespaces language-lawyer

考虑以下程序:

namespace foo {
namespace sub {
int f();
} // namespace sub 
} // namespace foo 

namespace bar {
namespace sub {

int g()  {
    using namespace foo;
    return sub::f() + 1;
}

} // namespace sub 
} // namespace bar 
Run Code Online (Sandbox Code Playgroud)

我希望这可以编译,但是 - 它没有:

$ g++-6 -c a.cpp
a.cpp: In function ‘int bar::sub::g()’:
a.cpp:12:9: error: ‘f’ is not a member of ‘bar::sub’
  return sub::f() + 1;
         ^~~
a.cpp:12:9: note: suggested alternative:
a.cpp:3:5: note:   ‘foo::sub::f’
 int f();
     ^
Run Code Online (Sandbox Code Playgroud)

也没有运气与铿锵:

$ clang++-6.0 -c a.cpp
a.cpp:12:9: error: no member named 'f' in namespace 'bar::sub'; did you mean 'foo::sub::f'?
        return sub::f() + 1; 
               ^~~~~~
               foo::sub::f
a.cpp:3:5: note: 'foo::sub::f' declared here
int f();
    ^
1 error generated.
Run Code Online (Sandbox Code Playgroud)

它也知道我想要的功能!

现在,我明白,如果我同时拥有a foo::sub::f和a ,那么事情可能会模棱两可bar::sub::f.但是 - 即使我明确要求使用函数,为什么还要bar::sub"隐藏" ?foo::subfoo::sub

要清楚答案可能是什么:

  • 解释为什么这是(最)合理的行为和我的期望是不合理的.
  • 它在C++标准中的这种方式的基本原理.
  • 引用标准说这需要是这样的; 也许上下文会帮助我理解为什么这个决定.

动机:我编写了我的代码,以便函数之前的最后一个命名空间是方法名称的一部分,在某种意义上,如果没有预先添加命名空间,你将无法理解函数的作用.因此,我希望能够说出来,而不是sub_f()像我所说的那样.没有预先设置更长的名称空间路径,这样做很难做到.Csub::f()

Jon*_*ely 5

[namespace.udir]说(强调我的):

-2- using-directive指定指定命名空间中的名称可以在using-directive出现在using-directive之后的范围内使用.在非限定名称查找(6.4.1)期间,名称看起来好像是在最近的封闭命名空间中声明的,其中包含using-directive和指定的命名空间. [注意:在此上下文中,"包含"表示"直接或间接包含". - 结束说明]

在您的示例中,"指定命名空间中的名称"只是foo::sub因为它是在命名空间中声明的唯一名称foo,而"包含using-directive和指定命名空间的最近的封闭命名空间"是全局命名空间.所以foo::sub出现(为了名称查找的目的)就好像它在全局命名空间中一样,如下所示:

namespace sub {
int f();
} // namespace sub 

namespace bar {
namespace sub {

int g()  {
    return sub::f() + 1;
}

} // namespace sub 
} // namespace bar 
Run Code Online (Sandbox Code Playgroud)

现在应该更明显的是sub::f找不到任何东西.sub::f通过查找来查找起始的名称sub.它没有在封闭的命名空间(它bar::sub)中找到它,因此它在下一个最里面的封闭范围(它是bar)中查找并找到它sub.此时,对于sub停止的名称查找,它不会查找使用指令foo::sub可以看到的全局命名空间.

名称查找的下一步尝试在找到f的范围内查找sub,但f在找到的命名空间中没有,因此名称查找失败.

using-directives不像为命名空间的每个成员添加using声明那样工作,它们比using声明"弱".这样做的原因是,在命名空间中真正声明的名称优先于使用using指令可见的名称.罗德里戈对相关问题的回答更详细地解释了这一点.