为什么两阶段查找无法选择“swap”的重载版本?

2023-11-26

我在学习这个令人着迷的答案 to a 微妙的问题关于实施的最佳实践swap用户定义类型的函数。 (我的问题最初是由讨论向命名空间添加类型的非法性std.)

我不会在此处重新打印上述链接答案中的代码片段。

相反,我想理解答案。

我在上面链接的答案在第一个代码片段下方指出了超载swap in namespace std(而不是专门化它在该命名空间中):

如果你的编译器打印出不同的东西,那么它就不是 正确实现模板的“两阶段查找”。

答案接着指出专门化swap in namespace std(相对于超载 it) 产生不同的结果(在以下情况下期望的结果专业化).

然而,答案还涉及一个额外的案例:专门用于用户定义的交换template class- 在这种情况下,同样无法达到预期的结果。

不幸的是,答案很简单states事实;它没有解释why.

有人可以详细说明该答案,并描述该答案中提供的两个特定代码片段中的查找过程:

  • 超载swap in namespace std对于用户定义的非模板类(如链接答案的第一个代码片段所示)

  • 专门化swap in namespace std对于用户定义的模板类(如链接答案的最终代码片段所示)

在这两种情况下,通用std::swap被调用,而不是用户定义的swap. Why?

(这将阐明两阶段查找的本质,以及实现用户定义的最佳实践swap;谢谢。)


序言包含大量标准语言

致电给swap()在示例中需要一个从属名称,因为它的参数begin[0] and begin[1]取决于模板参数T周围的algorithm()函数模板。标准中定义了此类从属名称的两阶段名称查找,如下所示:

14.6.4.2 候选函数 [temp.dep.candidate]

1 对于后缀表达式是从属名称的函数调用, 使用通常的查找规则(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)中的后缀表达式为一个 不合格的 ID,通常情况下未考虑的其他名称空间 可以搜索不合格的查找(3.4.1),并且在这些命名空间中, 命名空间范围的友元函数或函数模板声明 (11.3) 可能会发现不可见的情况。这些修改对 搜索取决于参数的类型(对于模板模板 参数,模板参数的命名空间)。

将标准应用于示例

The 第一个例子 calls exp::swap()。这不是从属名称,不需要两阶段名称查找。因为对 swap 的调用是限定的,所以会进行普通查找,仅查找通用的swap(T&, T&)函数模板。

The 第二个例子(@HowardHinnant 称之为“现代解决方案”)调用swap()并且也有过载swap(A&, A&)在与 where 相同的命名空间中class A生存(在本例中为全局命名空间)。因为对 swap 的调用是无限定的,所以普通查找和 ADL 都发生在定义点(同样只查找泛型)swap(T&, T&))但另一个 ADL 发生在实例化点(即exp::algorithm()正在被召唤main())这就会出现swap(A&, A&)这是在重载解析期间更好的匹配。

到目前为止,一切都很好。现在再演一遍:第三个例子 calls swap()并且有一个专业template<> swap(A&, A&) inside namespace exp。查找与第二个示例中的相同,但现在 ADL 不选择模板专业化,因为它不在关联的命名空间中class A。然而,尽管专业template<> swap(A&, A&)在重载决策期间不起作用,它仍然在使用时实例化。

最后,第四个例子 calls swap()并且有过载template<class T> swap(A<T>&, A<T>&) inside namespace exp for template<class T> class A生活在全局命名空间中。查找与第三个示例中的相同,并且 ADL 再次不会拾取过载swap(A<T>&, A<T>&)因为它不在类模板的关联命名空间中A<T>。在这种情况下,也没有必须在使用时实例化的专门化,因此通用swap(T&, T&)正在这里被调用。

结论

即使您不允许添加新的重载namespace std,并且只有显式专业化,它甚至无法工作,因为两阶段名称查找的各种复杂性。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么两阶段查找无法选择“swap”的重载版本? 的相关文章

随机推荐