缺省情况下swap动作可由标准程序库提供的swap算法完成。
namespace std
{
template <typename T>
void swap (T& a, T& b)
{
T temp(a);
a=b;
b= temp;
}
}
但是使用默认额swap效率非常低,例如:
class WidgetImpl
{
public:
...
private:
int a,b,c; //可能还有更多数据,意味着复制时间很长
std::vector<double> v;
...
};
class Widget
{
public :
Widget(const Widget & rhs);
Widget & operator = (const Widget & rhs )
{
...
*pImpl = *(rhs.pImpl);
...
}
...
private:
WidgetImpl * pImpl;
};
一旦要置换两个Widget对象值,我们唯一需要做的就是置换其pImpl,但缺省的swap算法不知道这一点。他不只复制三个Widget,还复制三个WidgetImpl对象。效率非常低。
我们希望告诉std::swap:当Widget被置换时真正该做的是置换其内部的pImpl指针:将是std::swap针对Widget特化。
例如:
namesapce std
{
template<>
void swap<Wideget>(Widget & a, Wideget & b) //这是特化版本。
{
swap(a.pImpl, b.pImpl);
}
}
函数一开始的“template<>"表示它是std::swap的一个特化版本。
通常我们不能够(不被允许)改变std命名空间内额任何东西,但是可以(被允许)为标准template( 如swap) 制造特化版本:
以上不能通过是因为它企图访问a和b内的pImpl指针,而那是private.我们可以将这个特化版本声明为friend,但和以往的规矩不太一样,我们令Widget声明为一个名为swap的public成员函数做真正的置换工作,然后将std::swap特化,令它用该成员函数:
class Widget
{
public:
...
void swap(Widget & other)
{
using std:: swap; //这个声明非常必要
swap(pImpl, other.pImpl);
}
...
};
namespace std
{
template<>
void swap<Widget> ( Widget &a , Widget & b)
{
a.swap(b);
}
} //可以通过!
偏特化:
namespace std
{
template<typeaname T >
void swap <Widget<T> >(Widget<T> &a , Widget<T> & b )
{
a.swap(a); //错误不合法!
}
}
我们企图偏特化一个模板函数,但是C++只允许我们对模板类偏特化。
当你打算偏特化一个模板函数时,通常做法是简单委为它添加一个重载版本:
namespace std
{
template<typename T>
void swap (Widget <T> & a ,Widget <T> & b)
{
a.swap(b):
}
}
但是这也不合法!一般而言,重载模板函数没有问题,但std是个特殊的命名空间,其管理规则也比较特殊,客户可以全特化std的template,但是不可以添加新的template到std里头。std的内容完全有C++标准委员会决定的。
答案很简单,我们还是声明一个非成员函数的swap让他调用成员函数的swap,但不再将那个非成员函数声明为std::swap的特化版或者重载版本:
namespac WidgetStuff
{
...
template<typename T>
class Widget{ ...}; //内含public的成员函数,调用private变量,调用下面的swap
...
template<typename T>
void swap(Wideget<T> & a, Widget<T> & b) //这里的swap不属于std命名空间
{
a.swap(b);
}
}
如果你希望调用T 专属版本,并在该版本不存在得情况下调用std内的版本时:
template<typename T>
void dosomething ( T & obj1, T & obj 2)
{
using std:: swap:
....
swap(obj1, obj2);
...
}
一旦编译器看到对swap得调用,他们便查找适当得swap并调用,C++名称查找法则确保找到全局作用域或者T所在得命名空间内得任何T专属得swap 。如果T是Widget并位于命名空间WidgetStuff内,编译器会找出WidgetStuff的Swap,如果没有T专属的swap存在,编译器就会使用std内的swap,着的感谢std::swap的声明式让std:;swap在函数内曝光。