c++ - std::unordered_map find() 操作在 GCC7 中不起作用

标签 c++

我正在将我的 C++ 应用程序从 GCC4.7 移植到 GCC7,并遇到了 std::hash_map find() 函数返回 null 的问题 map 中存在的键的结果。

现有代码:

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() 即使对于 map 中存在的键也会返回 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 并不简单,需要大量的工作工作。

我想知道是否有任何解决方法可以使其与 map 键的现有 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;
};

using 构造函数中添加的 unordered_map 使得可以以更简单的方式构建 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;
    }
};

关于c++ - std::unordered_map find() 操作在 GCC7 中不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67709794/

相关文章:

c++ - 为什么 C++11 不支持这样的名称查找?

c++ - 无法将 'this' 指针转换为类

c++ - 如何将 vector 重新排列为列而不是行?

C++ "No appropriate default constructor available"

c++ - std::map 和函数指针作为具有不同签名的值

c++ - 如何在 gdb 中为 C++ 对象的所有构造函数同时设置断点?

c++ - 什么是 undefined reference / Unresolved external symbol 错误,我该如何解决?

c++ - 有没有办法使用strcpy将一个字符串数组复制到另一个字符串或另一个数组中?

c++ - 如何在类中分配多维数组?当行 > 列时会抛出错误?

c++ - wxWidgets 中的可切换菜单栏(何时隐藏?)