我不希望编译这个代码,但确实如此。我的理解是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);
}
答案 0 :(得分:8)
参数d
是main
的本地参数。 datatype
只是ns::data
类型的别名,因此d
的类型为ns::data
。
ns::data
是ns
命名空间的[直接]成员,因此ns
命名空间中的此函数将被视为ADL。
答案 1 :(得分:4)
此行为由标准(我的重点)指定:
3.4.2依赖于参数的名称查找[basic.lookup.argdep]
2 - 对于函数调用中的每个参数类型
T
,都有一组零个或多个关联的命名空间以及一组零个或多个要考虑的关联类。命名空间和类的集合完全由函数参数的类型(以及任何模板模板参数的命名空间)决定。 用于指定类型的Typedef名称和 using-declarations
这有点不幸;这意味着,例如代码(改编自ADL with typedefs from another namespace):
std::vector<int> v;
count(v.begin(), v.end(), 0);
将取决于std::vector<T>::iterator
是T *
的typedef还是namespace std
中的某种类型的有效性和含义。
答案 2 :(得分:4)
其他答案已经提供了理由,如果不是理由:
typedef
是类型的别名,它将由编译器解析为实际类型。依赖于参数的查找是基于底层类型而不是typedef。
此设计决策的基本原理实际上是ADL使用该语言的原因。 ADL已添加到语言中以支持运算符重载。在任何其他用例中,用户可以显式地声明函数的命名空间,但是在运算符重载的情况下会导致复杂的代码反直觉:
std::string s("Hi");
std::cout.operator<<(s); // or is it std::operator<<(std::cout,s)??
因此,语言添加了查找规则,以便在不同的命名空间中查找运算符(和函数),特别是在函数参数的命名空间中。在这种情况下,如果std::
operator<<
不是std::string
的成员,则std::cout
内。相同的行为被扩展到同一名称空间中的所有自由函数(为什么不呢?),允许类型的接口不仅包括成员函数,还包括同一名称空间中的自由函数。
现在,如果您专注于此,目的是访问属于该类型接口的一部分的函数,并使用类型定义。在不同的命名空间中添加typedef时,您只需创建一个简写来引用原始类型。随类型提供的所有函数(例如operator<<(std::ostream&,MyType)
)都位于原始命名空间中,而typedef
的命名空间中没有。您希望 ADL查看定义了真实类型的命名空间,而不是创建别名的位置。
答案 3 :(得分:3)
要注意的要点是typedef
不会引入新类型,而是另一种类型的同义词。因此d
的类型为ns::data
。
现在ADL适用,函数func
将在命名空间ns
中找到。
添加:要防止ADL,您可以编写(func)(d)
。