序言包含大量标准语言
致电给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
,并且只有显式专业化,它甚至无法工作,因为两阶段名称查找的各种复杂性。