考虑如下代码
class WidgetImp1 { //针对Widget数据设计的class,细节不重要 public: //... private: int a, b, c; std::vector<double> v; }; class Widget { public: Widget(const Widet& rhs); Widget& operator=(const Widget& rhs) { //... *pImpl = *(rhs.pImpl); } private: WidgetImpl* pImpl; };当我们要交换Widget对象值的时候,我们唯一要做的就是交换其成员pImpl指针,但是缺省的swap算法不知道这一点,不但会复制三个widget,还会复制三个WidgetImpl对象,效率十分低下;
我们希望告诉std::swap,当交换Widget时,真正要做的是交换内部的pImpl指针;办法就是将std::swap针对Widget特化
namespace std { template<> void swap<Widget> (Widget& a, Widget& b){ swap(a.pImpl, b.pImpl); } }template<>代表它是针对std::swap的一个全特化,函数名称之后的<Widget>代表这一特化版本系针对"T是Widget"而设计的;通常我们不能够(不被允许)改变std命名空间内的任何东西,但是可以(被允许)为标准templates(如swap)制造特化版本;
上述特化swap还无法通过编译,因为pImpl是私有成员,无法访问,解决办法是令Widget声明一个名为swap的public成员函数做真正的交换工作,然后做特化工作
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); } }现在我们假设Widget和WidgetImpl都是class templates而非classes:
template<typename T> class WidgetImpl {//...} template<typename T> class Widget {//...}我们可以像之前一样在Widget放个swap成员函数,但是在特化std::swap时遇到了麻烦:
namespace std { template<typename T> void swap< Widget<T> >(Widget<T>& a, Widget<T>& b){ // 错误! a.swap(b); } }我们企图偏特化一个function template,但是C++之允许对class templates偏特化;当你打算偏特化一个function template时惯常做法是简单地为它添加一个重载版本:
namespace std { template<typename T> void swap(Widget<T>& a, Widget<T>& b){ // 重载版本 a.swap(b); }; }一般而言,重载function template没有问题,但是std是个特殊的命名空间,客户可以全特化std内的template但不可以添加新的templates到std里头;解决办法是我们还是声明一个 non-member的swap让它调用member的swap,但不再将那个non-member声明为std::swap的特化版本:
namespace WidgetStuff { // ... template<typename f> class Widget{ //...}; //... template<typename T> void swap(Widget<T>& a, Widget<T>& b){ // 这里并不属于std命名空间 a.swap(b); }; }最后调用swap时应该针对std::swap使用using声明式,然后调用swap并且不带任何"命名空间资格修饰",如下:
template<typename T> void doSomething(T& a,T& b) { using std::swap // 令std::swap在此函数内可用 //... swap(a, b); //... }