Dan*_*aum 9 c++ templates swap argument-dependent-lookup c++11
我正在研究这个关于实现用户定义类型函数的最佳实践的微妙问题的这个有趣的答案.(我的问题最初的动机是讨论将名称添加到命名空间的非法性.)swapstd
我不会在这里重新打印上面链接的答案中的代码片段.
相反,我想了解答案.
答案我上面的状态联系在一起,第一代码片段之下,在关于超载swap的namespace std(而不是专门在的命名空间):
如果您的编译器打印出不同的东西,那么它就不能正确地为模板实现"两阶段查找".
然后答案指出,专门swap研究namespace std(而不是重载它)会产生不同的结果(在专业化的情况下是期望的结果).
但是,答案继续进行另外一种情况:为用户定义的模板类专门交换 - 在这种情况下,再次无法实现所需的结果.
不幸的是,答案只是陈述事实; 它没有解释原因.
有人可以详细说明这个答案,并在该答案中提供的两个特定代码片段中描述查找过程:
过载swap在namespace std为用户定义的非模板类(如在联答复的第一代码段)
专注swap于namespace std用户定义的模板类(如链接答案的最终代码段)
在这两种情况下,std::swap都会调用泛型,而不是用户定义的泛型swap.为什么?
(这将阐明两阶段查找的本质,以及实现用户定义swap的最佳实践的原因;谢谢.)
要在通话swap()中的示例需要一个从属名称,因为它的参数begin[0],并begin[1]依赖于模板参数T周围的algorithm()函数模板.此类依赖名称的两阶段名称查找在标准中定义如下:
14.6.4.2候选函数[temp.dep.candidate]
1对于post fi x-expression是从属名称的函数调用,使用通常的查找规则(3.4.1,3.4.2)找到候选函数,除了:
- 对于使用非标准名称查找(3.4.1)的查找部分,仅找到模板定义上下文中的函数声明.
- 对于使用关联命名空间(3.4.2)的查找部分,仅找到在模板定义上下文或模板实例化上下文中找到的函数声明.
非限定查找由.定义
3.4.1不合法的名称查找[basic.lookup.unqual]
1在3.4.1中列出的所有情况下,在每个相应类别中列出的顺序中搜索范围; 一旦找到名称的声明,名称查找就会结束.如果没有找到声明,该程序就是格式错误.
和参数依赖查找(ADL)为
3.4.2依赖于参数的名称查找[basic.lookup.argdep]
1当函数调用(5.2.2)中的postfix-expression是非限定id时,可以搜索在通常的非限定查找(3.4.1)中未考虑的其他名称空间,并在这些名称空间中查找名称空间范围的朋友函数或可以找到不可见的函数模板声明(11.3).对搜索的这些修改取决于参数的类型(以及模板模板参数,模板参数的命名空间).
第一个例子叫exp::swap().这不是依赖名称,不需要两阶段名称查找.因为对swap的调用是合格的,所以发生普通查找,它只查找通用swap(T&, T&)函数模板.
在第二个例子(什么@HowardHinnant通话"现代解决方案")要求swap(),并且还具有过载swap(A&, A&)在相同的命名空间,其中class A居住(在这种情况下,全局命名空间).因为对swap的调用是不合格的,所以普通查找和ADL都发生在定义点(再次只发现泛型swap(T&, T&)),但另一个ADL发生在实例化时(即exp::algorithm()调用的位置main()),这swap(A&, A&)就是在重载分辨率期间是更好的匹配.
到现在为止还挺好.现在为安可:第三个例子调用swap()并且template<> swap(A&, A&)内部有一个专门化namespace exp.查找与第二个示例中的查找相同,但现在ADL不会选择模板特化,因为它不在关联的命名空间中class A.但是,即使专业化template<> swap(A&, A&)在重载解析期间不起作用,它仍然在使用时被实例化.
最后,第四个示例调用swap()并在template<class T> swap(A<T>&, A<T>&)内部具有重载namespace exp以template<class T> class A生活在全局命名空间中.查找与第三个示例中的查找相同,并且ADL再次没有获取重载,swap(A<T>&, A<T>&)因为它不在类模板的关联命名空间中A<T>.在这种情况下,也没有必须在使用点实例化的特化,因此通用swap(T&, T&)在这里被调用.
即使您不允许添加新的重载namespace std,也只允许显式的特殊化,但由于两阶段名称查找的各种复杂性,它甚至无法工作.