具有命名空间的编译器的有趣行为
假设代码如下:
#include 使用命名空间标准;命名空间 X{A类{};void f(A a){}void g(int a){}}int main(){X::A a;F A);克(5);}
当我编译代码时,出现如下编译错误:
<块引用>main.cpp:在函数int main()"中:
main.cpp: 错误: 'g' 未在此范围内声明
所以函数 f
被完美编译,但 g
不是.如何?它们都属于同一个命名空间.编译器是否从 X::A
类型的参数推断出函数 f
属于 X
命名空间?在这种情况下,编译器的行为如何?
这适用于函数调用表达式:
f(a);
因为 X::A
所属的命名空间包含在函数 f
的查找中,因为 参数依赖查找(ADL),cppreference对ADL的解释如下:><块引用>
参数相关查找,也称为 ADL 或 Koenig 查找,是用于查找不合格函数名称的一组规则函数调用表达式,包括隐式函数调用重载运算符.这些函数名称在除了范围和名称空间之外,它们的参数的名称空间由通常的非限定名称查找考虑.
依赖于参数的查找使得使用定义的运算符成为可能在不同的命名空间中
这在 草案 C++ 标准中有所涉及 部分 3.4.2
依赖参数的名字查找:
当函数调用(5.2.2)中的postfix-expression是unqualified-id时,不考虑其他命名空间在通常的非限定查找 (3.4.1) 期间可能会被搜索, 在这些命名空间中,namespace-scope可能会发现友元函数或函数模板声明 (11.3) 不可见
接着说:
<块引用>对于函数调用中的每个参数类型 T,都有一组零个或多个关联的命名空间和一个要考虑的零个或多个相关类的集合.命名空间和类的集合被确定完全由函数参数的类型(以及任何模板模板参数的命名空间)决定.
并包括以下项目符号:
<块引用>如果T是一个类类型(包括联合),其关联的类是:类本身;它是一个类会员,如果有的话;及其直接和间接基类.其关联的命名空间是命名空间其中关联的类是成员.[...]
进一步提供与您的问题类似的示例:
命名空间 NS {类 T { };无效 f(T);void g(T, int);}NS::T 参数;void g(NS::T, float);int main() {f(参数);//OK: 调用 NS::fextern void g(NS::T, float);g(参数,1);//OK: 调用 g(NS::T, float)}
函数调用表达式:
g(5);
不起作用,因为 ADL 没有为基本类型的参数添加任何命名空间.
Herb Sutter 在 Gotw #30 和 中介绍了 ADL://www.gotw.ca/publications/mill02.htm" rel="nofollow">课堂上有什么?- 接口原则.
Assume the following code:
#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);
}
When I compile the code, the following compile error occurs:
main.cpp: In function 'int main()':
main.cpp: error: 'g' was not declared in this scope
So the function f
is compiled perfectly, but g
isn't. How? Both of them belong to the same namespace. Does the compiler deduce that function f
belongs to the X
namespace from the argument of type X::A
? How does compiler behave in such cases?
This works for the function call expression:
f(a);
because the namespace that X::A
belongs to is included in the lookup for the function f
due to argument dependent lookup(ADL), cppreference explains ADL as follows:
Argument-dependent lookup, also known as ADL, or Koenig lookup, is the set of rules for looking up the unqualified function names in function-call expressions, including implicit function calls to overloaded operators. These function names are looked up in the namespaces of their arguments in addition to the scopes and namespaces considered by the usual unqualified name lookup.
Argument-dependent lookup makes it possible to use operators defined in a different namespace
This is covered in the draft C++ standard section 3.4.2
Argument-dependent name lookup:
When the postfix-expression in a function call (5.2.2) is an unqualified-id, other namespaces not considered during the usual unqualified lookup (3.4.1) may be searched, and in those namespaces, namespace-scope friend function or function template declarations (11.3) not otherwise visible may be found
and goes on to say:
For each argument type T in the function call, there is a set of zero or more associated namespaces and a set of zero or more associated classes to be considered. The sets of namespaces and classes is determined entirely by the types of the function arguments (and the namespace of any template template argument).
and includes the following bullet:
If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the namespaces of which its associated classes are members.[...]
and further down provides a similar example to your problem:
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)
}
The function call expression:
g(5);
does not work because ADL does not add any namespaces for arguments that are fundamental types.
Herb Sutter covers ADL in Gotw #30 and in What's In a Class? - The Interface Principle.
相关文章