#include<algorithm>
#include<iostream>
#include<cstdio>
#include<vector>
class WidgetImpl{
//......
std::vector<int>v;
};
class Widget
{
public:
void swap(Widget& other){
//原书给的是
using std::swap;
swap(pImpl,other.pImpl);
//std::swap(pImpl,other.pImpl);
//调用std标准中的swap函数,也能得出同样的结果
//可能很多人也觉得直接调用std标准中的函数就可以了
//书后面讲到,用上面的写法
//编译器会首先在全局作用域或者T所在的命名空间
//这里就是WidgetImpl类型所在的命名空间中
//查找是否有专属的swap,如果没有才调用标准中的swap
//使得程序更为鲁棒
}
private:
WidgetImpl *pImpl;
//这时候直接交换指针所指内容就可以了
//如果调用默认的swap函数,就会深拷贝vector
};
namespace std{
template<>
void swap<Widget>(Widget& a,Widget& b){
//我们不能改变std命名空间内的任何东西
//但可以为标准templates制造特化版本
//使它属于我们自己的class
a.swap(b);
//为了函数的封装性,尽量少声明friend-function
//所以提供一个接口,供特化版本swap使用
//并且与STL容器有一致性
cout << "call Widget swap function" << endl;
}
}
int main()
{
Widget a,b;
using std::swap;
swap(a,b);
}
如果刚才的Widget和WidgetImpl都是class templates而非classes。。。
再刚才那样子写就不对了。。。c++不允许在function templates身上偏特化,而允许对class templates身上偏特化。。。
惯常,要想偏特化一个function templates,可以添加一个重在版本。。。
但是客户可以全特化std内的templates,但不可以添加新的templates(classes或functions或其他东西)到std里面
所以我们只能放到其他命名空间中或者global中(但是在global中放太多东西会导致不整齐吧,原书作者这样说的吧)
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<vector>
namespace WidgetStuff{
template<typename T>
class WidgetImpl{
//......
std::vector<T>v;
};
template<typename T>
class Widget
{
public:
void swap(Widget& other){
using std::swap;
swap(pImpl,other.pImpl);
}
private:
WidgetImpl<T> *pImpl;
};
template<typename T>
void swap(Widget<T>& a,Widget<T>& b){
a.swap(b);
//non-member swap函数
//这里并不属于std命名空间
std::cout << "call Widget swap function" << std::endl;
}
}
template<typename T>
void doSomething(T& lsh,T& rsh)
{
using std::swap;
//C++名称查找法则确保将找到global作用域和T所在命名空间内
//的任何T专属的swap,如果没有T专属的,则使用std内的swap
puts("&&&&&&&&&&&&&&&");
swap(lsh,rsh);
puts("---------------");
//这里如果调用std::swap()那便强迫编译器只认std内的swap
}
int main()
{
int a,b;
doSomething(a,b);
WidgetStuff::Widget<int>c,d;
doSomething(c,d);
}
成员版swap绝对不抛出异常,这个约束只施于成员版
当你写下自定义版的swap,往往不光提供高效置换对象的方法,而且不抛出异常。因为高效swap几乎总是基于对内置类型的操作,而内置类型上的操作绝对不会抛出异常