当内部范围不起作用时,为什么编译器不采用命名空间名称?

Kla*_*aim 7 c++ gcc visual-c++ c++11

我认为我很了解名字查找(在观看了几个关于它的视频并阅读了很多内容之后)但我只是遇到了这个案例:

#include <iostream>

namespace test{

  struct Id
  {};

  void do_something( const Id& ){  std::cout << "Hello, World!" << std::endl; }

  class Test
  {
  public:

    void do_something() { std::cout << "WTF!" << std::endl;  }

    void run()
    {
      Id id;
      do_something( id ); // doesn't compile
    }

  };

}

int main()
{
    test::Test my_test;
  my_test.run();
}
Run Code Online (Sandbox Code Playgroud)

指向的行不会编译(在GCC4.8和VC11U2上),因为它试图使用成员函数 test::Test::do_something()而不是命名空间作用域test::do_something( const Id& ),这似乎是唯一可能的候选者.

显然,成员函数名称隐藏了命名空间范围的名称,这对我来说是令人惊讶的,因为我记得在其他上下文中使用几乎相似的代码而没有产生此问题(但最终条件可能会非常不同).

我的问题是:这些编译器是否符合标准?

(不幸的是,通过阅读标准文档很难理解名称查找,所以我需要专家确认)

Jon*_*ely 6

我的问题是:这些编译器是否符合标准?

是.在重载决策决定哪些函数是可行的候选者(包括检查参数的数量)之前,编译器首先必须进行名称查找,以找到所有候选者,可行且不可行.在您的示例名称中,查找在找到成员后停止,do_something()因此重载决策永远不会有机会决定命名空间范围是否可行.

3.4.1 [basic.lookup.unqual]/1:"在3.4.1中列出的所有情况下,搜索范围按照每个相应类别中列出的顺序搜索声明;名称查找在声明后立即结束找到了这个名字."

3.4.1 [basic.lookup.unqual]第8段列出了搜索名称的上下文,甚至还有一个能够准确回答你问题的例子.Test在封闭命名空间之前搜索范围,并且如第1段所述,"一旦为名称找到声明,名称查找就会结束".


Mar*_*k B 1

编译器将收集所有“候选名称”(除非涉及 ADL,否则它们将来自同一范围),然后尝试选择最佳匹配(如果有)。在任何情况下,失败的匹配都不会导致它尝试从备用范围中查找其他候选名称。

这与编译器首先执行重载解析,然后检查成员的公共/私有以查看它是否确实可访问的方式非常相似。

g++ 有一个方便的-Wshadow选项来寻找阴影(不过我不确定它会特别警告这个选项)。

  • @Klaim:命名空间函数的问题更适合窃取您的成员调用? (2认同)
  • “但是为什么不使用显式的 this-&gt; 来修复这个问题”那么你就必须用 `this-&gt;` 来编写每个成员函数,即使当前没有外部函数窃取查找。否则,将来对外部代码的更改可能会改变代码的含义。@DyP (2认同)
  • 我**不**同意到处写`this-&gt;`。这些规则**不** _“禁止用属于类成员的通用代码编写通用非成员函数”_,如果您打算包含非成员名称,则放置 `using test::do_something;`否则它应该找到“最近”的名称(即从当前范围开始并向外进入封闭范围) (2认同)