文章目錄
- unordered_map使用MFC的CString作為鍵值遇到C2056和C2064錯誤
- 問題出現的背景
- 解決方案
- 總結
unordered_map使用MFC的CString作為鍵值遇到C2056和C2064錯誤
問題出現的背景
在我的一個老工程項目中,使用C++的std::unordered_map
時,使用了MFC的CString
作為鍵值類型,遇到編譯錯誤C2056和C2064。這些錯誤通常與哈希函數和比較操作符的缺失有關。
此前,項目使用了如下代碼實現CString
的哈希函數和比較操作符:
// CString 哈希函數結構體,用于unordered_map
struct CStrHashFunc
{size_t operator()(const CString &str) const{return hash_value(static_cast<LPCTSTR>(str));}
};
代碼是VS2015環境下實現的,然后當項目代碼升級遷移到VS2022時,隨著VC++版本的升級,hash_value
這種非標準函數不再可用,參考unordered_map
使用std::string
作為鍵的辦法,示例代碼如下:
#include <iostream>
#include <string>
#include <unordered_map>int main()
{std::unordered_map<std::string, double> mymap = {{"mom",5.4},{"dad",6.1},{"bro",5.9},{"姐姐",5.5 }};std::string input;std::cout << "who? ";getline(std::cin, input);std::unordered_map<std::string, double>::const_iterator got = mymap.find(input);if (got == mymap.end())std::cout << "not found";elsestd::cout << got->first << " is " << got->second;std::cout << std::endl;return 0;
}
上述代碼以unordered_map
的查找為例,使用了std::string
作為鍵值類型,并且可以直接使用std::hash<std::string>
,不需要額外定義哈希函數。
然而,我們將這種思路放到CString
上時,直接使用std::hash<CString>
會導致編譯錯誤C2056和C2064,因為標準庫并沒有為MFC的CString
提供默認的哈希函數和比較操作符。
解決方案
為了解決這個問題,我們需要為CString
自定義哈希函數和比較操作符。以下是一個示例代碼,展示了如何實現這一點,該代碼可以在VS2022中編譯通過,且功能正常:
// CString 比較函數結構體,用于unordered_map
struct CStrCmp
{bool operator()(const CString &str1, const CString &str2) const{return str1 == str2;}
};// CString 哈希函數結構體,用于unordered_map
struct CStringHasher
{template <typename BaseType, class StringTraits>size_t operator()(const CStringT<BaseType, StringTraits>& _Keyval) const noexcept{ // hash _Keyval to size_t value by pseudorandomizing transformreturn std::_Hash_array_representation(_Keyval.GetString(), _Keyval.GetLength());}
};// 應用示例:
unordered_map<CString, int, CStringHasher, CStrCmp> m_filemap;
unordered_map<CString, int, CStringHasher, CStrCmp>::const_iterator itr = m_filemap.find(filename);
在上述代碼中,我們定義了兩個結構體:
CStrCmp
:用于比較兩個CString
對象是否相等,重載了operator()
。CStringHasher
:用于計算CString
的哈希值,重載了operator()
,利用了CStringT
的成員函數GetString()
和GetLength()
來獲取字符串內容和長度,并調用了標準庫的哈希函數。
當然,我們也可以在namespace std
(std命名空間)中定義這哈希和比較運算(事實上這種情況下我們只需要定義哈希,因為這種模式下會默認使用CString
自帶的比較運算):
namespace std
{template <typename BaseType, class StringTraits>struct hash<CStringT<BaseType, StringTraits>>{ // hash functor for CStringT<BaseType, StringTraits>size_t operator()(const CStringT<BaseType, StringTraits>& _Keyval) const noexcept{ // hash _Keyval to size_t value by pseudorandomizing transformreturn (_Hash_array_representation(_Keyval.GetString(), _Keyval.GetLength()));}};
} // namespace std
調用示例代碼如下:
std::unordered_map<CString, int> myMap = { {L"acad.lsp", 1}, {L"acaddoc.lsp", 1}, {L"acad.mnl", 2} };
std::unordered_map<CString, int>::const_iterator it = myMap.find(L"acad.lsp");
該代碼編譯和執行均正常。
總結
通過為MFC的CString
自定義哈希函數和比較操作符,我們成功解決了在使用std::unordered_map
時遇到的編譯錯誤C2056和C2064的問題。這種方法不僅適用于CString
,也可以推廣到其他自定義類型,只要為它們提供合適的哈希函數和比較操作符即可。這樣,我們就能充分利用unordered_map
的高效查找性能,同時避免編譯錯誤。
然而,CString
類??不是線程安全??的。在多線程環境中,如果多個線程同時操作??同一個?? CString
實例(例如作為 unordered_map
的鍵并進行修改),可能會導致內存地址錯誤或進程異常退出。