默认版本
namespace std
{
template<typename T>
void swap(T & a,T & b)
{
T temp(a);
a=b;
b=temp;
}
}
如果类采用指针存储数据,则默认交换方式会变得低效。因为只用交换两个指针便能实现两个对象的交换。因此需要特化版本的swap,由于指针是私有成员,因此需要一个公有成员函数swap,完成实际的交换。然后由特化版本的swap调用该公有成员函数。我们不能修改namespace std的内容但我们可以为我们自定义类型完全特化标准模板。
全特化版本
class WidgetImpl
{
private:
int a,b,c;
std::vector<int> v;
};
class Widget
{
public:
Widget(const Widget & rhs);
Widget & operator=(const Widget & rhs)
{
*p=*rhs.p;
return *this;
}
void swap(Widget & other)
{
using std::swap;
swap(p, other.p);
}
private:
WidgetImpl * p;
};
namespace std
{
template<>
void swap<Widget>(Widget & a,Widget & b)
{
a.swap(b);
}
}
如果处理的是类模板而不是类,则情况又不同了。因为函数模板(默认版本swap)只能全特化而不能偏特化。
因此不能写成
namespace std
{
template<typename T>
void swap<Widget<T>>(Widget<T> & a,Widget<T> & b)
{
a.swap(b);
}
}
面对这种情况,惯常做法是为它添加一个重载版本。我们可以全特化std内的template,但不能给std添加新的template。因此不能将非成员swap声明为std::swap偏特化版本或者重载版本。采用的策略为将非成员swap和模板类放在同一个命名空间。
非成员函数,非std::swap版本
namespace WidgetStuff
{
template<typename T>
class Widget
{
};
template<typename T>
void swap(Widget<T> & a,Widget<T> & b)
{
a.swap(b);
}
}
如果不额外使用某个命名空间,那么也可以,但会都处于global命名空间。这种做法对class和class template都行得通。但对于class还是应该特化一个std::swap版本。因为编写代码时可能强制限定std::swap版本。
有这么多版本的swap,那么实际调用哪个版本呢?你希望的是调用T的专属版本,并在该版本不存在时调用std内的一般版本。
template<typename T>
void dosometing(T & a,T & b)
{
using std::swap;
swap(a,b);
}
首先会在T所在命名空间或者global作用域内寻找任何T专属swap,如果没有找到,由于using std::swap;的存在,则会调用std内的版本,首先看有没有特化版本,然后再是一般版本。
综上,一共有default swap、member swap、non-member swap、std::swap全特化版本。