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::被拉了进来。整洁的