我正在研究this fascinating answer到subtle question的有关最佳实践的最佳实践,以实现用户定义类型的swap
函数。 (我的问题最初是由discussion of the illegality of adding types to namespace std
引起的。)
在这里,我不会从上面链接的答案中重新打印代码段。
相反,我想了解答案。
我在上面的代码中链接的答案是在第一个代码段的下面,关于swap
中的重载namespace std
(而不是在该命名空间中进行专门化):
然后答案继续指出,中的特化swap
(而不是重载)产生了不同的结果(在特化的情况下需要的结果)。
但是,答案还会有另外一种情况:专门用于用户定义的模板类的交换-在这种情况下,同样无法获得所需的结果。
不幸的是,答案只是陈述事实。它没有解释为什么是。
有人可以详细说明该答案,并在该答案中提供的两个特定代码段中描述查找过程:namespace std
中的swap
(如链接的答案的第一个代码段)namespace std
中将swap
专门用于用户定义的模板类(如链接答案的最终代码段)
在这两种情况下,都将调用通用namespace std
,而不是用户定义的std::swap
。为什么?
(这将阐明两阶段查找的性质以及best practice for implementing user-defined swap
的原因;谢谢。)
最佳答案
带有大量Standardese的序言
在此示例中,对swap()
的调用需要一个从属名称,因为其参数begin[0]
和begin[1]
取决于周围T
函数模板的模板参数algorithm()
。在标准中对此类从属名称的两阶段名称查找的定义如下:
14.6.4.2候选函数[temp.dep.candidate]
不合格的查找定义为
3.4.1不合格的名称查找[basic.lookup.unqual]
和依赖于参数的查找(ADL)为
3.4.2依赖于参数的名称查找[basic.lookup.argdep]
将标准应用于示例
第一个示例调用exp::swap()
。这不是从属名称,不需要两阶段名称查找。因为对swap的调用是合格的,所以将进行普通查找,该查找仅查找通用swap(T&, T&)
函数模板。
的第二个示例(@HowardHinnant称为“现代解决方案”)调用swap()
,并且在与swap(A&, A&)
所在位置相同的 namespace (此例中为全局 namespace )中也具有重载的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
,以便生存在全局 namespace 中的template<class T> class A
。查找与第三个示例中的查找相同,并且ADL也不拾取重载swap(A<T>&, A<T>&)
,因为它不在类模板A<T>
的关联命名空间中。并且在这种情况下,也没有必须在使用时实例化的特化,因此在这里调用了通用swap(T&, T&)
。
结论
即使不允许您向namespace std
添加新的重载,并且仅允许显式的特殊化,由于两阶段名称查找的各种复杂性,它甚至无法工作。
关于c++ - 为什么两阶段查找无法选择 'swap'的重载版本?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/21384604/