OP中的代码与以下代码等价:
using std::swap;
namespace ns {
template <typename T>
void swap(T& a, T& b) {
swap(a, b);
}
}
为什么?因为像
using namespace std;
这样的
using-directives在C++14 [namespace.udir]p2中有一种非常奇特的行为:
using-directive指定了被提名命名空间中的名称可以在using-directive出现之后的范围内使用。在未经限定的名称查找中,这些名称似乎是在最近的封闭命名空间中声明的,该命名空间包含using-directive和被提名命名空间。
包含命名空间
std
和函数块作用域
ns::swap
的最近封闭命名空间是全局命名空间。
另一方面,
using-declarations如
using std::swap;
确实将名称引入它们出现的范围内,而不是某个封闭范围。
函数调用表达式(例如
swap(a, b)
)的查找称为未经限定的查找。标识符
swap
没有与任何命名空间或类名限定,与
ns::swap
相反,
ns::
已通过限定限定。潜在函数名称的未经限定查找由两部分组成:纯未经限定查找和参数相关的查找。
纯未经限定查找在包含名称的最近封闭范围处停止。在OP的示例中,如上所示的等效转换所示,包含名称
swap
声明的最近范围是命名空间
ns
。全局范围不会被搜索,也不会通过纯未经限定查找找到
std::swap
。
参数相关的查找搜索与参数类型相关联的所有范围(这里仅为命名空间和类)。对于类类型,类被声明的命名空间是关联作用域。C++标准库类型(例如
std::vector<int>
)与命名空间
std
相关联,因此,如果
T
是C++标准库类型,则可以通过参数相关的查找找到表达式
swap(a, b)
的
std::swap
。同样,您自己的类类型允许在它们被声明的命名空间中找到
swap
函数:
namespace N2 {
class MyClass {};
void swap(MyClass&, MyClass&);
}
因此,如果参数相关查找没有找到比纯粹的非限定查找更好的匹配,你最终会递归地调用 ns::swap。
在调用未限定的 swap(即 swap(a, b) 而不是 std::swap(a, b))背后的想法是,通过参数相关查找找到的函数被认为比 std::swap 更专业。对于自己的类模板类型特化函数模板(由于禁止部分函数模板特化),无法实现,也不能向命名空间 std 添加自定义重载。std::swap 的通用版本通常实现如下:
template<typename T>
void swap(T& a, T& b)
{
T tmp( move(a) );
a = move(b);
b = move(tmp);
}
这需要一个移动构造加上两个移动赋值,有可能甚至会回退到拷贝。因此,你可以为与这些类型相关联的命名空间提供一个专门的swap函数。你的专门版本可以利用你自己类型的某些属性或私有访问。
using namespace std;
的using-directives具有非常特殊的行为;它们不是使用声明(using-declarations),如using std::swap
。 - dypusing std::swap;
,我们告诉它在块作用域停止 -ns::swap
将不会被找到,因为它是封闭命名空间作用域的成员。另一方面,参数相关名称查找搜索与参数类型相关联的所有作用域。 - dyp