我不希望这个代码编译,但确实如此.我的理解是func(d)它在全局命名空间中查找名为"func"的函数,但也在任何传入参数的命名空间中查找(参数依赖查找)
但在这种情况下,参数位于全局命名空间中.那么为什么在ns命名空间中找到"func"呢?是否有特殊规则说如果参数类型是typedef,那么它使用底层类型的命名空间而不是实际参数的命名空间?
这似乎是真的,但我找不到任何支持这个......这是预期的行为吗?
namespace ns
{
struct data {};
void func(ns::data item) {}
};
// Create an alias "datatype" in the global namespace for ns::data
typedef ns::data datatype;
int main()
{
datatype d;
func(d);
}
Run Code Online (Sandbox Code Playgroud)
参数d是本地的main.datatype只是类型的别名,ns::data所以d有类型ns::data.
ns::data是ns命名空间的[直接]成员,因此命名空间中的此函数ns将被视为ADL.
其他答案已经提供了原因,如果没有理由的话:
A
typedef是类型的别名,编译器会将其解析为实际类型。基于参数的查找是基于基础类型而不是typedef完成的。
该设计决策的基本原理实际上就是ADL采用该语言的原因。ADL已添加到语言中,以支持操作员重载。在任何其他用例中,用户都可以显式声明函数的名称空间,但是在运算符重载的情况下,这将导致卷积的代码非常直观:
std::string s("Hi");
std::cout.operator<<(s); // or is it std::operator<<(std::cout,s)??
Run Code Online (Sandbox Code Playgroud)
因此,该语言为查找添加了规则,以便在不同的名称空间(尤其是在函数自变量的名称空间)中查找运算符(和函数)。在这种情况下std::,如果operator<<a std::string是的成员,则内部std::cout。相同的行为扩展到相同名称空间中的所有自由函数(为什么不这样做),从而允许类型的接口不仅包含成员函数,而且还包含相同名称空间中的自由函数。
现在,如果您专注于此,目的是访问属于该类型接口的功能,并且这些功能是用该类型定义的。在其他命名空间中添加typedef时,您只是在创建一个引用原始类型的简写形式。类型提供的所有功能(例如operator<<(std::ostream&,MyType))都在原始名称空间中,而在的名称空间中则没有typedef。您希望 ADL查看定义实型的名称空间,而不是创建别名的名称空间。
此行为由标准指定(我强调):
3.4.2依赖于参数的名称查找[basic.lookup.argdep]
2-对于
T函数调用中的每个参数类型,要考虑一组零个或多个关联的命名空间和一组零个或多个关联类。名称空间和类的集合完全由函数参数的类型(以及任何模板模板参数的名称空间)确定。用于指定类型的Typedef名称和using- 声明对该集合没有帮助。
这有点不幸;这意味着例如代码(从ADL改编自另一个命名空间的typedef):
std::vector<int> v;
count(v.begin(), v.end(), 0);
Run Code Online (Sandbox Code Playgroud)
它的有效性和含义将取决于是否std::vector<T>::iterator为typedef T *或in namespace std。