template<typename T>
void swap(T& a, T& b)
{
T temp(a); // 拷貝構(gòu)造
a = b; // 拷貝賦值運(yùn)算符
b = temp; // 拷貝賦值運(yùn)算符
}
一個(gè)基本的swap函數(shù)大概如上所述,但是對(duì)某些類型而言。典型比如: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;
}
一旦需要置換兩個(gè)Widget對(duì)象值,我們只需要交換pImpl指針,但缺省的swap算法不知道這一點(diǎn),它不止拷貝三個(gè)Widget,還拷貝三個(gè)WidgetImpl對(duì)象。
我們希望能夠告訴std::swap,當(dāng)Widgets被置換時(shí)真正該做的是置換其內(nèi)部的pImpl指針,確切的做法就是:將std::swap針對(duì)Widget特化。
namespace std {
template<>
void swap<Widget>(Widget& a, Widget& b)
{
swap(a.pImpl, b.pImpl);
}
}
通常我們不被允許改變std命名空間內(nèi)的任何東西,但可以為標(biāo)準(zhǔn)的template制造特化版本。
雖然上面的代碼看起來(lái)達(dá)到我們的目的,但實(shí)際上這個(gè)函數(shù)無(wú)法通過編譯,因?yàn)樗髨D訪問 a 和 b 內(nèi)的Impl指針,而那是private的。
但我們可以在內(nèi)部實(shí)現(xiàn)一個(gè)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容器有一致性,因?yàn)樗械腟TL容器也都提供有public swap成員函數(shù)和std::swap特化版本。
using std::swap
不拋異常
成員版swap最好實(shí)現(xiàn)成不要拋異常,因?yàn)?code>swap的一個(gè)最好應(yīng)用是幫助class提供異常安全保障。此技術(shù)基于一個(gè)假設(shè):成員版的swap絕不拋異常。但這一約束只約束于成員版,不可施行于非成員版,因?yàn)?code>swap缺省版本是以拷貝構(gòu)造和拷貝賦值操作符為基礎(chǔ),而一般情況下兩者都允許拋異常。
總結(jié)
- 如果
swap的缺省實(shí)現(xiàn)對(duì)你的class或class template提供可接受的效率。那么不需要做額外的操作。
如果swap的缺省實(shí)現(xiàn)版本效率不足,試著做下面的事情:- 提供一個(gè)
public swap成員函數(shù),讓它高效的置換你的類型的兩個(gè)對(duì)象值,并且這個(gè)swap函數(shù)絕不能拋出異常。 - 在你的
class或template所在的命名空間內(nèi)提供一個(gè)non-member swap,并令它調(diào)用上述swap成員函數(shù)。 - 如果你在編寫一個(gè)
class而非class template,為你的class特化std::swap。
- 提供一個(gè)