Item 25 考慮實現乙個不拋異常的swap

2022-05-28 04:39:08 字數 1676 閱讀 5300

swap 函式最初由 stl 引入,已經成為異常安全程式設計的關鍵函式, 同時也是解決自賦值問題的通用機制。 std 中它的基本實現是很直觀的:

namespace std

}

可以看到,上述 swap 是通過賦值和拷貝構造實現的。所以 std::swap 並未提供異常安全, 但由於 swap 操作的重要性,我們應當為自定義的類實現異常安全的 swap。

先不提異常安全,有時對自定義型別而言 std::swap 並不高效。 比如採用 pimpl idiom 設計的類中,只需要交換實現物件的指標即可:

class widgetimpl;

class widget ;

namespace std

}

上述**是不能編譯的,因為 pimpl 是私有成員!所以,widget 應當提供乙個 swap 成員函式或友元函式。 慣例上會提供乙個成員函式:

class widget 

};

接著我們繼續特化 std::swap,在這個通用的 swap 中呼叫那個成員函式:

namespace std 

}

到此為止,我們得到了完美的 swap **。上述實現與stl容器是一致的:提供共有 swap 成員函式, 並特化std::swap 來呼叫那個成員函式

當 widget 是類模板時,情況會更加複雜。按照上面的 swap實現方式,你可能會這樣寫:

templateclass widgetimpl ;

templateclass widget ;

namespace std

}

上述**不能通過編譯。c++允許偏特化類模板,卻不允許偏特化函式模板(雖然在有些編譯器中可以編譯)。 所以我們乾脆不偏特化了,我們來過載 std::swap 函式模板:

namespace std 

}

這裡過載了 std::swap,相當於在 std 命名空間新增了乙個函式模板。這在 c++ 標準中是不允許的! c++ 標準中,客戶只能特化 std 中的模板,但不允許在 std 命名空間中新增任何新的模板。 上述**雖然在有些編譯器中可以編譯,但會引發未定義的行為。

別在 std 下新增 swap 模板函式,把 swap 定義在 widget 所在的命名空間中:

namespace widgetstuff ;

templatevoid swap(widget& a, widget& b)

}

任何地方在兩個 widget 上呼叫 swap時,c++根據其 argument-dependent lookup 會找到widgetstuff命名空間下的 swap。

說到這裡,你可能會問如果我希望優先呼叫 widgetstuff::swap,如果未定義則取呼叫 std::swap,那麼應該如何寫呢? 看**:

templatevoid dosomething(t& obj1, t& obj2)
此時,c++編譯器還是會優先呼叫指定了 t 的 std::swap,其次是 obj1 的型別 t 所在命名空間下的對應 swap 函式, 最後才會匹配 std::swap 的預設實現。

條款25 考慮寫出乙個不拋異常的swap函式

總結 如果 std swap 對於你的型別來說是低效的,請提供乙個 swap 成員函式,並確保你的 swap 不會丟擲異常。如果你提供乙個成員 swap,請同時提供乙個呼叫成員swap的非成員swap。對於類 非模板 還要特化 std swap。呼叫swap時,請為std swap使用乙個using...

條款25 考慮寫出乙個不丟擲異常的swap函式

swap原本只是stl的一部分,後面成為異常安全程式設計的脊柱,及處理自我賦值安全性的乙個常見機制。例子 標準程式庫提供的swap演算法 namespace s td 要求 型別t支援copying 通過copying建構函式和copyassignment操作符完成 上述 主要涉及三個物件的複製,但...

條款25 考慮寫出乙個不丟擲異常的swap函式

首先本篇部落格的主要思想是 系統自帶的swap函式有時候不能滿足我們的需求,所以在一些情況下我們就需要自己去寫swap函式。此條款的主要內容就是告訴你該如何去寫你要的swap函式,下面開始正文來好好地介紹一下此條款的內容 1.首先來看一下庫裡面給的swap函式的原型 可以看出來標準程式庫提供的swa...