在開發的過程中需要遍歷一個unordered_map
然后把他的迭代器傳給另一個對象:
class A;
class B {
public:void deal(const std::pair<int, A>& item);
};
std::unordered_map<int, A> mp;
B b;
for (auto &pr : mp) {b.deal(pr);
}
在我的項目中A類管理了系統資源,我震驚地發現經過deal
函數處理后A類所管理的資源會被釋放(調用了析構函數)。這讓我百思不得其解,我只是傳遞了一個引用而已啊,為什么會發生析構呢?經過調試發現在調用deal
函數的時候發生了pair
的拷貝操作,由于A類的拷貝構造是共享資源,所以在deal
函數的參數pr
析構的時候釋放了資源。
那么問題就變成了為什么將mp
的元素pr
傳遞給deal
就會發生拷貝?我們知道C++中普通引用要求兩者的類型必須一致,而常量引用卻可以發生類型轉換,莫非是因為形參和實參的類型不一致?由于range循環的元素pr
是推斷出來的,那么的確有可能出現這種情況。接下來我們的問題就是搞清楚auto &pr
是什么類型,然后修改deal
的形參類型即可。(當然也可以將deal
變成模板成員函數,然后用萬能引用也可以解決問題)。
《Effective Morden C++》的條款4向我們介紹了如何查看型別推導的結果,有三種方法,分別是依賴IDE、依賴編譯器診斷信息和在運行時輸出。我使用的是Clion進行開發,還是很靠譜地顯示了pr
的類型為:std::pair<const int, A>&
,而我們在類B中聲明的形參為const std::pair<int, A>&
,怪不得發生了拷貝構造。 于是我們修改B中的形參就可以啦。
如果IDE沒有這么智能,那么還是可以依賴我們的好兄弟編譯器的,《Effective Morden C++》中介紹了一種很trick的方法就是使用一個沒有定義的模板類或者模板函數,只要我們試圖使用這個模板,就會誘發一個錯誤信息,而編譯器肯定會在錯誤信息中加上類型的名字。
于是我寫了一個簡單的測試函數:
template<typename T>
void printType(T t);
經過運行,編譯器發出了錯誤警告:
undefined reference to `void edward::printType<std::pair<int const, edward::A> >(std::pair<int const, edward::A>)’
我也嘗試了第三種看起來最優雅的運行時輸出,書中噴了一下標準庫的typeid
的實現,推薦使用boost
庫中的boost::typeindex::type_id_with_cvr<T>().pretty_name()
進行輸出。但是我可能沒有正確安裝好boost庫,導致找到這個函數。
總之,我們要千萬小心map
和unordered_map
的元素類型為std::pair<const key_type, mapped_type>
,如果忘記const
可能會不小心產生拷貝操作,不僅僅會帶來性能損耗,而且可能會產生RuntimeError。