关于C++依赖参数查找(ADL)标准条款的困惑及示例求证
理解C++ ADL中typedef/using的行为:澄清标准表述与实际代码的差异
你的困惑完全可以理解——ISO标准的措辞有时候确实比较绕,我们把那两条规则拆解清楚,再结合你的代码分析,就能明白到底是怎么回事了。
首先先把你提到的标准原文列出来:
- "用于指定类型的typedef名称和using声明不加入此集合";
- "考虑关联命名空间时,查找方式与将关联命名空间用作限定符时的查找(6.4.3.2)相同,除了:关联命名空间中的所有using-directive都被忽略……"
第一条规则:别误解"不加入此集合"的含义
这里的核心是搞清楚**"此集合"指的是什么**:它指的是ADL过程中会被纳入查找范围的关联命名空间/实体的集合,而不是说typedef/using指向的类型本身会被ADL彻底忽略。
换成人话解释:
- 当你用
N::C(一个typedef)作为参数类型时,ADL不会把typedef所在的命名空间N加到要查找的命名空间列表里; - 但它会顺着typedef找到背后的原始类型
N2::B,然后把N2(原始类型所在的命名空间)加入ADL的查找范围——这才是ADL真正要去搜索的地方。
第二条规则:忽略关联命名空间内的using-directive,和typedef无关
这条规则是说,当ADL进入某个关联命名空间(比如你的代码里的N2)查找时,该命名空间内部的using namespace X;这类指令不会生效。举个例子,如果N2里写了using namespace std;,ADL不会因此去std里找函数,但这和typedef指向的类型没有任何关系——它只是限制了关联命名空间内部的using指令的作用,不会影响原始类型的关联命名空间被纳入查找。
结合你的代码具体分析
看你的示例代码:
#include <iostream> using namespace std; namespace N2 { struct B {}; template <typename T> void func (const T&) {cout << __PRETTY_FUNCTION__ << endl;} }; namespace N { typedef N2::B C; } void tfunc (N::C) {} int main () { func(tfunc); }
当你在main里调用func(tfunc)时,编译器的查找过程是这样的:
- 编译器需要找到
func的定义,触发ADL机制; - ADL会分析
func的参数类型:这里参数是tfunc,它的类型是void(N::C)(一个接受N::C的函数类型); - 对于函数类型的参数,ADL会进一步拆解其参数的类型——也就是
N::C; N::C是N2::B的typedef,所以ADL会把N2(B所在的命名空间)列为要查找的关联命名空间;- 在
N2里找到了template <typename T> void func(const T&),这个模板可以匹配tfunc的类型,所以代码成功编译运行。
总结一下:标准的规则是不把typedef所在的命名空间纳入ADL查找范围,但完全保留了typedef指向的原始类型的关联命名空间的查找——这就是你的代码能找到N2::func的原因,和你最初的误解正好相反。
内容的提问来源于stack exchange,提问作者Baykov Nikita
相关产品推荐
相关产品推荐

