一、std::swap
std::swap面对指针(如pImpl写法)时效率低,因此需要重写swap
1.1 如何重写swap
//pImpl写法的Widget
class WidgetImpl{
public:
...
void swap(Widget& other){
using std::swap;
swap(pImpl,other.pImpl);
}
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;
};
//全特化标准库swap
namespace std{
template <>
void swap<Widget>(Widget& a,Widget& b){
a.swap(b);
}
}
如果希望交换的并非是class而是 class template
- C++只允许对class templates偏特化,不允许对function templates身上进行偏特化,偏特化一个function template 时,惯常的做法是简单的为它添加一个重载版本
//为函数模版提供重载版本
namespace std{
template <typename T>
void swap(Widget<T>& a,Widget<T>& b){
a.swap(b);
}
}
- std命名空间内可以全特化templates,但是不能添加新的templates(或classes或functions或任何其他东西),因此在class tempalte相同的命名空间中声明swap
namespace WidgetStuff{
template<typename T>
class Widget{...};
...
template<typename T>
void swap(Widget<T>& a,Widget<T>& b){
a.swap(b);
}
}
- C++名称查找法则:1)确保找到global作用域或T所在之命名空间内的任何T专属的swap。如果T是Widget并位于命名空间WidgetStuff内,编译器会使用实参取决之查找规则找出WidgetStuff内的swap。如果没有T专属之swap存在,编译器就会使用std内的swap。
- non-member版本的swap已经通用,提供std空间内的特化版本是为了防止会有客户使用
std::swap 修饰限定法。 - 成员版本的swap不要抛出异常:高效率的swao几乎总是基于内置类型的操作,而内置类型上的操作绝对不会抛出异常。
1.2 总结
- 提供成员版本的swap
- 提供non-member 版本的swap
- 如果正在编写一个class(而非class template),提供特化的std::swap
|