带名称空间的编译器的有趣行为

时间:2014-09-22 14:14:54

标签: c++ compiler-errors namespaces argument-dependent-lookup

假设以下代码:

#include <iostream>
using namespace std;

namespace X
{
  class A{};

  void f(A a){}

  void g(int a){}
}

int main()
{
  X::A a;
  f(a);
  g(5);
}

编译代码时,会发生以下编译错误:

  

main.cpp:在函数&#39; int main()&#39;:
  main.cpp:错误:&#39; g&#39;未在此范围内声明

所以函数f编译得很完美,但g并非如此。怎么样?它们都属于同一名称空间。编译器是否从f类型的参数中推断出函数X属于X::A命名空间?编译器在这种情况下如何表现?

3 个答案:

答案 0 :(得分:27)

X::A a;
f(a);

Argument-Dependent Lookup (也称为Koenig Lookup)而起作用。 a是名称空间A内的类X的对象,当编译器搜索可匹配的函数f时,它会查看名称空间X在这种情况下。有关详细信息,请参阅Argument Dependent Lookup

答案 1 :(得分:19)

这适用于函数调用表达式:

f(a);

因为X::A由于argument dependent lookup(ADL)而对f}函数的查找中包含3.4.2所属的命名空间,所以cppreference解释 ADL ,如下所示:

  

依赖于参数的查找,也称为ADL或Koenig查找,是   查找不合格函数名称的规则集   函数调用表达式,包括对函数的隐式函数调用   重载运营商。这些函数名称在中查找   除了作用域和名称空间之外,还有其参数的名称空间   通过通常的非限定名称查找。

     

依赖于参数的查找可以使用定义的运算符   在不同的命名空间

draft C++ standard部分namespace NS { class T { }; void f(T); void g(T, int); } NS::T parm; void g(NS::T, float); int main() { f(parm); // OK: calls NS::f extern void g(NS::T, float); g(parm, 1); // OK: calls g(NS::T, float) } 依赖于参数的名称查找中涵盖了这一点:

  

函数调用(5.2.2)中的postfix-expression是非限定id时,不考虑其他名称空间   在通常的非限定查找(3.4.1)期间,可以搜索,并在那些命名空间中,命名空间范围   友情功能或功能模板声明(11.3)可能无法看到

继续说:

  

对于函数调用中的每个参数类型T,有一组零个或多个关联的命名空间和一个   要考虑的零个或多个关联类的集合。确定名称空间和类的集合   完全由函数参数的类型(以及任何模板模板参数的名称空间)。

并包含以下项目符号:

  

如果T是类类型(包括联合),则其关联的类是:类本身;它所属的类   会员,如果有的话;及其直接和间接基类。 其关联的命名空间是命名空间   其关联类是成员。[...]

并进一步向下提供了与您的问题类似的示例:

g(5);

函数调用表达式:

{{1}}

不起作用,因为 ADL 不会为基本类型的参数添加任何名称空间。

Herb Sutter在Gotw #30What's In a Class? - The Interface Principle中涵盖了ADL。

答案 2 :(得分:6)

当代码f(a)时,编译器在void f(A a){}中找到函数namespace X,因为ADL(依赖于参数的查找,也称为Koenig查找)。

A在名称空间X中声明,因此当编译器需要查找f的定义时,它包含来自该名称空间的可能性,因为对象a类型A的类型在该命名空间中(如声明X::A a;)。

另一方面,int中未声明namespace X,因此namespace X未包含在查找中。由于找不到f的相应函数,因此无法编译。