C++ 具有名称空间的编译器的有趣行为

C++ 具有名称空间的编译器的有趣行为,c++,compiler-errors,namespaces,argument-dependent-lookup,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); } #包括 使用名称空间std; 名称空间X { A类{}; 无效f(A){} void g(int a){} } int main() { X::A; f(a); g(5);

假设以下代码:

#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);
}
#包括
使用名称空间std;
名称空间X
{
A类{};
无效f(A){}
void g(int a){}
}
int main()
{
X::A;
f(a);
g(5);
}
编译代码时,会出现以下编译错误:

main.cpp:在函数“int main()”中:
main.cpp:错误:未在此作用域中声明“g”

因此,函数
f
的编译是完美的,而
g
则不是。怎么用?它们都属于同一名称空间。编译器是否从类型为
X::A
的参数推断出函数
f
属于
X
命名空间?编译器在这种情况下是如何工作的?

当代码
f(a)
时,编译器在
名称空间X
中发现函数
void f(a){}
,这是因为(依赖于参数的查找,也称为查找)

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

另一方面,
int
未在
名称空间X
中声明,因此
名称空间X
不包括在查找中。由于未找到
f
的对应函数,因此无法编译

X::A a;
f(a);

由于依赖于参数的查找(也称为Koenig查找)而起作用
a
是命名空间
X
中类
a
的对象,在这种情况下,当编译器搜索可匹配的函数
f
时,它将查找命名空间
X
。有关更多信息,请参阅。

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

f(a);
g(5);
由于函数
f
的查找中包含
X::A
所属的名称空间,因此cppreference对ADL的解释如下:

参数相关查找,也称为ADL或Koenig查找,是 用于在中查找非限定函数名的规则集 函数调用表达式,包括对 重载运算符。这些函数名在 除了作用域和名称空间之外,它们的参数的名称空间 由通常的非限定名称查找考虑

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

这在
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)
}
函数调用表达式:

f(a);
g(5);
不起作用,因为ADL不为基本类型的参数添加任何名称空间


Herb Sutter在文章中和文章中介绍了ADL。

依赖于参数的查找示例中的
extern
会影响什么吗?@dyp ugh,谢谢你指出,我希望每个人在阅读别人的答案时都能观察到。我总是对阅读你的答案感兴趣,因为它们通常是非常全面的,而且您使用的资源范围很广。我刚开始理解第一句话有点困难(你知道,介绍常常是一个简单的摘要/概述)。Dyp C++是我的工作,也是我喜欢做的事情,因此写完整的答案让我诚实和学习。我的工作并不总是很出色,但我很乐意根据建设性的反馈做出改进。@0x499602D2因此
extern
不会影响结果,
g
是隐式
extern
,但
g
的本地声明确实很重要。我昨天一定很慢,我今天早上就明白了为什么它运行得很快。这就是为什么当我认为需要前缀时,我看到std::被拉了进来。整洁的