【问题标题】:std::unordered_map find() operation not working in GCC7std::unordered_map find() 操作在 GCC7 中不起作用
【发布时间】:2021-05-27 05:42:58
【问题描述】:

我正在将我的 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 * 数据类型一起使用。对此的任何帮助将不胜感激。

【问题讨论】:

  • find() 永远不会返回 null。请包括实际输出
  • 您还需要一个自定义哈希函数。默认情况下std::unordered_map 将散列指针(而不是它们指向的)。因此,如果两个文字 c 字符串相同但保存在内存中的不同地址,则它们的哈希值不会相同。常量折叠的变化可能会产生您所看到的差异。
  • std::hash&lt;const char*&gt; 看起来很可疑。请参阅cppreference:“C 字符串没有专门化。std::hash 生成指针值(内存地址)的哈希值,它不检查任何字符数组的内容。”
  • 发布的代码在 gcc 4.7 中和 gcc 7 中一样损坏。
  • @karthik.zorfy 只有在哈希值相同时才使用 Equals。哈希找到桶然后等于检查桶中的每个项目。如果哈希不起作用/损坏,则等于无关紧要。

标签: c++


【解决方案1】:

您已经发现 std::hash&lt;const char*&gt; 对实际指针进行哈希处理,而不是它指向的 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;
};

unordered_maps 构造函数中添加的using 使得以更简单的方式构造地图成为可能:

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;
    }
};

【讨论】:

  • 特德,谢谢你的回答。正如我在问题中提到的,这是一个非常大的代码库,将数据类型从 const char * 更改为其他类型需要大量工作,我正在寻找一种解决方法以使其与 const char * 键类型一起使用.正如人们所建议的那样,我似乎可以通过定义自定义哈希函数来使用const char *
  • @karthik.zorfy 好的,我改了答案。
  • 显然std::string_view 仅在 c++17 或更高版本中可用,我无法在我的项目中使用它。不过,我接受了你的回答。
  • @karthik.zorfy 是的,好的,我添加了一个部分。如果你想要我,我可以添加一个实际的哈希函数来替换答案中的 std::string 哈希。
  • 我想出了下面的自定义哈希函数。您可以一次查看并添加您的 cmets。如果这看起来不错,请将其添加到您的答案中。可能对别人有用。 ``` struct customHash { inline size_t operator()(const char* str) const { size_t result = 0; for (; *str != '\0'; str++) { result = 5 * result + static_cast(*str); } 返回结果; } }; ```
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-18
  • 2012-07-09
  • 1970-01-01
  • 1970-01-01
  • 2016-09-30
  • 1970-01-01
相关资源
最近更新 更多