unordered_map使用MFC的CString作为键值遇到C2056和C2064错误
文章目录
- 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
的键并进行修改),可能会导致内存地址错误或进程异常退出。