我正在研究this fascinating answersubtle 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/

    10-13 05:34