template<typename T>
void swap(T& a, T& b)
{
T temp(a); // 拷貝構(gòu)造
a = b; // 拷貝賦值運(yùn)算符
b = temp; // 拷貝賦值運(yùn)算符
}
一個基本的swap
函數(shù)大概如上所述蘸际,但是對某些類型而言。典型比如:pimpl手法(pointer to implementation)徒扶,這些復(fù)制沒有必要粮彤。
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
算法不知道這一點(diǎn)导坟,它不止拷貝三個Widget
,還拷貝三個WidgetImpl
對象圈澈。
我們希望能夠告訴std::swap
惫周,當(dāng)Widgets
被置換時真正該做的是置換其內(nèi)部的pImpl指針,確切的做法就是:將std::swap
針對Widget
特化康栈。
namespace std {
template<>
void swap<Widget>(Widget& a, Widget& b)
{
swap(a.pImpl, b.pImpl);
}
}
通常我們不被允許改變std
命名空間內(nèi)的任何東西递递,但可以為標(biāo)準(zhǔn)的template
制造特化版本。
雖然上面的代碼看起來達(dá)到我們的目的谅将,但實(shí)際上這個函數(shù)無法通過編譯漾狼,因為它企圖訪問 a 和 b 內(nèi)的Impl指針,而那是private
的饥臂。
但我們可以在內(nèi)部實(shí)現(xiàn)一個swap
的public
成員函數(shù)做真正的置換工作逊躁,然后將std::swap
特化,令它調(diào)用成員函數(shù):
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);
}
}
這種做法不僅能通過編譯隅熙,還與STL容器有一致性稽煤,因為所有的STL容器也都提供有public swap
成員函數(shù)和std::swap
特化版本。
using std::swap
不拋異常
成員版swap
最好實(shí)現(xiàn)成不要拋異常囚戚,因為swap
的一個最好應(yīng)用是幫助class
提供異常安全保障酵熙。此技術(shù)基于一個假設(shè):成員版的swap
絕不拋異常。但這一約束只約束于成員版驰坊,不可施行于非成員版匾二,因為swap
缺省版本是以拷貝構(gòu)造和拷貝賦值操作符為基礎(chǔ),而一般情況下兩者都允許拋異常。
總結(jié)
- 如果
swap
的缺省實(shí)現(xiàn)對你的class
或class template
提供可接受的效率察藐。那么不需要做額外的操作皮璧。
如果swap
的缺省實(shí)現(xiàn)版本效率不足,試著做下面的事情:- 提供一個
public swap
成員函數(shù)分飞,讓它高效的置換你的類型的兩個對象值悴务,并且這個swap
函數(shù)絕不能拋出異常。 - 在你的
class
或template
所在的命名空間內(nèi)提供一個non-member swap
譬猫,并令它調(diào)用上述swap
成員函數(shù)讯檐。 - 如果你在編寫一個
class
而非class template
,為你的class
特化std::swap
染服。
- 提供一個