c++ - std::unordered_map find() 操作在 GCC7 中不起作用
问题描述
我正在将我的 c++ 应用程序从 GCC4.7 移植到 GCC7 并遇到一个问题,即std::hash_map
find()
函数返回null
映射中存在的键的结果。
现有代码:
struct eqfunc {
bool operator()(const char* const &s1, const char* const &s2) const {
std::cout << "eqfunc in action " << s1 << " - " << s2 << std::endl;
return strcmp(s1,s2) == 0;
}
};
template <typename T> class customMap : public std::hash_map<const char*,T,std::hash<const char*>,eqfunc> {};
customMap<const char*> cstmMap;
std::cout << "Insert abc" << std::endl;
cstmMap["abc"] = "ABC";
std::cout << "Insert def" << std::endl;
cstmMap["def"] = "DEF";
std::cout << "Insert xyz" << std::endl;
cstmMap["xyz"] = "XYZ";
std::cout << "Find def in cstmMap" << std::endl;
string findString("def");
customMap<const char*>::iterator ptr = cstmMap.find((char *)findString.c_str());
LOG_INFO("output ptr %s", ptr);
这在 GCC4.7 平台上运行良好。当我将代码移植到 GCC7 时,我注意到 find()null
甚至对于地图中存在的键也会返回结果的行为。
GCC7 中的示例运行输出
Insert abc
Insert def
Insert xyz
Find def in cstmMap
output ptr (null)
更新std::hash_map
为std::unordered_map
也不起作用:
template <typename T> class customMap : public std::unordered_map<const char*,T,std::hash<const char*>,eqfunc> {};
我注意到使用的另一个奇怪行为std::unordered_map
是eqfunc
在多次运行中没有以一致的模式执行
样品 1 次运行
Insert abc
Insert def
eqfunc in action def - abc
Insert xyz
Find def in cstmMap
eqfunc in action def - xyz
output ptr (null)
样品 2 运行
Insert abc
Insert def
eqfunc in action def - abc
Insert xyz
Find def in cstmMap
output ptr (null)
注意:这是非常大的代码库,更改const char *
为std::string
并不简单,需要大量工作。
我想知道是否有任何解决方法可以使其与const char *
地图键的现有数据类型一起使用。对此的任何帮助将不胜感激。
解决方案
您已经发现std::hash<const char*>
散列实际指针 - 而不是它指向的 C 字符串。有时"def"
和第二个"def"
实际上将具有相同的指针值。这取决于编译器如何优化它。
要使用 C 字符串,您需要为 C 字符串提供哈希函子。这是一个例子:
#include <string_view>
struct cstring_hash {
size_t operator()(std::string_view str) const {
return std::hash<std::string_view>{}(str);
}
};
并重新定义容器:
template <typename T>
class customMap : public std::unordered_map<const char*, T, cstring_hash, eqfunc> {
// To be able to use ctors:
using std::unordered_map<const char*, T, cstring_hash, eqfunc>::unordered_map;
};
添加s 构造函数使得以更简单的方式构造地图成为可能using
:unordered_map
int main() {
customMap<const char*> cstmMap{
{"abc", "ABC"},
{"def", "DEF"},
{"xyz", "XYZ"},
};
std::string findString("def");
auto ptr = cstmMap.find(findString.c_str());
std::cout << ptr->second << '\n'; // prints DEF
}
如果您使用的是 C++17 之前的 C++ 版本,则可以cstring_hash
选择一个足够好的哈希函数来替换。这是一个可能完成这项工作的人:
namespace detail {
static const auto S = // shift constant
sizeof(size_t) < sizeof(uint64_t) ? 16u : 32u;
static const auto C = // multiplication constant
sizeof(size_t) < sizeof(uint64_t) ? 23456789u : 0xBB67AE8584CAA73Bull;
}
#if __cpp_constexpr >= 201304L
#define RELAXEDCONSTEXPR constexpr
#else
#define RELAXEDCONSTEXPR
#endif
struct cstring_hash {
RELAXEDCONSTEXPR size_t operator()(const char *s) const {
size_t h = 0;
for(; *s; ++s) {
h = h * detail::C + static_cast<unsigned char>(*s);
h ^= h >> detail::S;
}
return h *= detail::C;
}
};
推荐阅读
- matlab - 在 Matlab 中使用 Golden Search 方法进行优化
- c# - 如何将对象从客户端发送到服务器并反向?
- certificate - 证书上的“签名算法”到底是什么意思?使用哪种签名算法来签署我的证书?
- c++ - 不能用作函数,返回前的最后一行是错误
- android - 如何配置 Android Gradle 项目以过滤资源?
- javascript - 设置状态的新 Promise 接收箭头函数
- computer-vision - 如何使用 Viola Jones 算法将人脸检测为感兴趣区域并将其裁剪到矩形框?
- python - 加入列表最后几个元素并将新加入的项目附加回列表的最快和 Pythonic 方式
- python - python - 如何在python中拆分不包含分隔符的连接字符串?
- javascript - 如何正确过滤数组