考虑如下代码
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);
//...
}