编译器在命名空间方面的有趣行为

33
假设以下代码为:
#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: 在函数'int main()'中:
main.cpp: error: 'g'在此作用域中未声明

所以函数f被完美编译,但g没有。这是为什么?它们都属于同一个命名空间。编译器是否从类型为X::A的参数中推断出函数f属于X命名空间?编译器在这种情况下会如何行动?

3个回答

28
X::A a;
f(a);

之所以能够正常工作是因为参数依赖查找(也称为Koenig查找)。a 是命名空间 X 内部类 A 的对象,当编译器寻找一个可匹配的函数 f 时,会在该情况下搜索命名空间 X。有关更多信息,请参见参数相关名称查找


所以这就是为什么我看到std::被引入,而我认为我需要前缀。很好。 - Captain Giraffe

19

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

f(a);

由于参数相关查找(ADL), 函数 f 的查找范围包括 X::A 所属的命名空间。cppreference 对 ADL 的解释如下:

参数相关查找(Argument-dependent lookup),又称 ADL 或 Koenig 查找,是查找函数调用表达式中未限定函数名的一组规则,包括对重载运算符的隐式函数调用。这些函数名在其参数的命名空间中进行查找,以及通常未限定名称查找所考虑的作用域和命名空间。

参数相关查找使得能够使用不同命名空间中定义的运算符。

这在C++ 草案标准中的第 3.4.2 节“参数相关名称查找”中有所涵盖。

当函数调用(5.2.2)中的后缀表达式为未限定ID时,可以搜索其他未在通常的未限定查找(3.4.1)期间考虑的命名空间,并且可以在这些命名空间中找到未在其他地方可见的命名空间范围友元函数或函数模板声明(11.3)。
对于函数调用中的每个参数类型T,都有一组零个或多个相关的命名空间和一组零个或多个相关的类需要考虑。命名空间和类的集合完全由函数参数的类型(以及任何模板模板参数的命名空间)确定。
如果T是一个类类型(包括联合体),它的相关类是:类本身;它是其成员的类(如果有);以及其直接和间接基类。其相关命名空间是其相关类所属的命名空间。[...]
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)
}

函数调用表达式:

g(5);

无法工作,因为ADL不会为基本类型参数添加任何命名空间。

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


在这个例子中,extern 会影响到任何东西吗? - David G
@dyp 哎呀,感谢您指出这一点,希望每个人阅读他人答案时都能像您这样细心。 - Shafik Yaghmour
我总是很感兴趣阅读你的答案,因为它们通常非常全面,并且你使用了广泛的来源。但是一开始我有些难以理解第一句话(你知道,介绍通常是一个简单的摘要/概述)。 - dyp
@dyp C++是我的工作,也是我喜欢做的事情,因此撰写全面的答案让我保持诚实并不断学习。我并不总是做得很好,但我很乐意根据建设性反馈进行改进。 - Shafik Yaghmour
@0x499602D2 所以 extern 不会影响结果,g 隐式地是 extern 的,但是 g 的局部声明确实很重要。昨天我可能真的很慢,今天早上我很快就想出了为什么它能工作的原因。 - Shafik Yaghmour
谢谢,这很有帮助。 - Gyapti Jain

6
当代码f(a)时,编译器会因为ADL(参数相关查找,也称Koenig查找)在namespace X中找到函数void f(A a){}
由于Anamespace X中声明,所以当编译器需要查找f的定义时,它会包括该命名空间中的可能性,因为类型为A的对象a在该命名空间中(如声明X::A a;)。
另一方面,int没有在namespace X中声明,因此不会包括该命名空间在查找中。由于没有找到相应的f函数,编译失败。

参数依赖查找 - Niall

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接