简体   繁体   English

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

[英]std::unordered_map find() operation not working in GCC7

I am working on porting my c++ application from GCC4.7 to GCC7 and ran into an issue where std::hash_map find() function returns null result for keys which are present in map. I am working on porting my c++ application from GCC4.7 to GCC7 and ran into an issue where std::hash_map find() function returns null result for keys which are present in map.

Existing code:现有代码:

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

This was working fine in GCC4.7 platform.这在 GCC4.7 平台上运行良好。 When I ported the code to GCC7, I noticed the behaviour where find() returns null result even for keys which are present in the map.当我将代码移植到 GCC7 时,我注意到 find() 返回null结果的行为,即使对于 map 中存在的键也是如此。

Sample run output in GCC7在 GCC7 中运行示例 output

Insert abc
Insert def
Insert xyz
Find def in cstmMap
output ptr (null)

Updated std::hash_map to std::unordered_map doesn't work either:std::hash_map更新为std::unordered_map也不起作用:

template <typename T> class customMap : public std::unordered_map<const char*,T,std::hash<const char*>,eqfunc> {};

Another weird behaviour I noticed using std::unordered_map is that eqfunc is not executed in consistent pattern across multiple runs我注意到使用std::unordered_map的另一个奇怪行为是eqfunc在多次运行中没有以一致的模式执行

Sample 1 run样品 1 次运行

Insert abc
Insert def
eqfunc in action def - abc
Insert xyz
Find def in cstmMap
eqfunc in action def - xyz
output ptr (null)

Sample 2 run样品 2 运行

Insert abc
Insert def
eqfunc in action def - abc
Insert xyz
Find def in cstmMap
output ptr (null)

NOTE: This is very large code base and changing const char * to std::string is not straightforward and requires a lot of work.注意:这是非常大的代码库,将const char *更改为std::string并不简单,需要大量工作。

I am wondering if there are any work arounds to get it working with existing const char * data type for the map's key.我想知道是否有任何解决方法可以使其与地图键的现有const char *数据类型一起使用。 Any help on this would be appreciated.对此的任何帮助将不胜感激。

You've discovered that std::hash<const char*> hashes the actual pointer - not the C string it points at.您已经发现std::hash<const char*>散列实际指针 - 而不是它指向的 C 字符串。 Sometimes "def" and the second "def" will actually have the same pointer value.有时"def"和第二个"def"实际上将具有相同的指针值。 It depends on how the compiler optimized it.这取决于编译器如何优化它。

To use C strings, you need to provide a hash functor for C strings.要使用 C 字符串,您需要为 C 字符串提供 hash 函子。 Here's one example:这是一个例子:

#include <string_view>

struct cstring_hash {
    size_t operator()(std::string_view str) const {
        return std::hash<std::string_view>{}(str);
    }
};

And redefine the container:并重新定义容器:

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

The added using of the unordered_map s constructors makes it possible to construct the map in a simpler way: unordered_map构造函数的添加using使得可以以更简单的方式构造 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
}

If you are using a C++ version prior to C++17, you could replace cstring_hash by picking a good enough hash function.如果您使用的是 C++17 之前的 C++ 版本,您可以通过选择足够好的 hashA384F1AB54.8E16 来替换cstring_hash Here's one that probably does the job:这是一个可能完成这项工作的人:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM