Effective C++ 第二十五條 Consider support for a non-throwing swap.
考慮寫(xiě)出一個(gè)不拋出異常的 swap 函數(shù)
swap 就是交換,當(dāng)我們初學(xué) C語(yǔ)言 或者 C++ 的時(shí)候,我們通常會(huì)自己寫(xiě)一個(gè)類(lèi)似于 swap 功能的函數(shù)
后文將采取C++ template相關(guān)代碼進(jìn)行介紹(書(shū)中也是如此),如果不明白 C++ 函數(shù)模版、類(lèi)模版、全特化、偏特化相關(guān)概念的話,請(qǐng)百度搜索一下。
這里只需要類(lèi)型 T 是支持 copy constructor 和 copy assignment 就能行。這種版本對(duì)于內(nèi)置類(lèi)型(int、double……)很好用,但是對(duì)于自定義的類(lèi)來(lái)說(shuō)就不一定了,如下
我們現(xiàn)在為 Widget 定義 swap 函數(shù),不需要交換所有內(nèi)容,只需要交換其中的 pImpl ,但是缺省的 swap 函數(shù)不知道這點(diǎn),會(huì)復(fù)制三個(gè) Widget 和三個(gè) WidgetImpl ,效率低下。
本例子表明,當(dāng)模版類(lèi)型是 Widget 的時(shí)候調(diào)用屬于 Widget 的專(zhuān)屬代碼,否則調(diào)用一般的 swap 代碼。本段代碼不能通過(guò)是因?yàn)?pImpl 是 private,不能被 non-member non-friend 函數(shù)調(diào)用。我們可以將這個(gè)版本的特化聲明為 friend ,但不符合以往的實(shí)現(xiàn)方式。采取如下方法
這種方式實(shí)現(xiàn)可以通過(guò)編譯還可以與 STL 容器保持一致性,因?yàn)樗?STL 容器都提供 public swap 函數(shù)和 std::swap特化版本。調(diào)用前者更好。
現(xiàn)在假設(shè) Widget 不是 class 而是 class template
這樣就無(wú)法通過(guò)編譯,因?yàn)檫@樣做是對(duì) function template 偏特化,C++不允許,C++只允許對(duì) class template 偏特化。如果打算對(duì) function template 偏特化,只需要簡(jiǎn)單的為其設(shè)置一個(gè)重載版本
這段代碼能夠通過(guò)編譯,但是最好不要放在 std 空間下,因?yàn)檫@不符合 C++ 標(biāo)準(zhǔn)委員會(huì)的決定。我們最好放在自己聲明的命名空間下。

編譯器會(huì)優(yōu)先在 global 和 class 所在的 namespace 尋找關(guān)于類(lèi)型 T 的 swap 特化函數(shù),如果找到了就使用,如果沒(méi)找到,就只能調(diào)用一般的 swap 函數(shù)。
總結(jié):
當(dāng) std::swap 對(duì)你的類(lèi)型效率不高時(shí),提供一個(gè) swap 成員函數(shù),并確定這個(gè)函數(shù)不拋出異常。
如果你提供一個(gè) member swap,也應(yīng)該提供一個(gè) non-member swap 用來(lái)調(diào)用前者。對(duì)于 classes (而非 templates),也應(yīng)該特化 std::swap。
調(diào)用 swap 時(shí)應(yīng)該針對(duì) std::swap 使用 using::std 聲明式,然后調(diào)用 swap 并且不帶任何命名空間資格修飾。
為 “用戶定義類(lèi)型” 進(jìn)行 std template 全特化是好的,但是千萬(wàn)不要在 std 內(nèi)加入某些對(duì) std 而言全新的東西。