Same key, multiple entries for std :: unordered_map?

I have a map inserting multiple values ​​with the same type key C string.

I would expect to have one entry with the specified key.

However, the card appears to take its address into account when uniquely identifying the key.

#include <cassert>
#include <iostream>
#include <string>
#include <unordered_map>

typedef char const* const MyKey;

/// @brief Hash function for StatementMap keys
///
/// Delegates to std::hash<std::string>.
struct MyMapHash {
public:
    size_t operator()(MyKey& key) const {
        return std::hash<std::string>{}(std::string(key));
    }
};

typedef std::unordered_map<MyKey, int, MyMapHash> MyMap;

int main()
{
    // Build std::strings to prevent optimizations on the addresses of
    // underlying C strings.
    std::string key1_s = "same";
    std::string key2_s = "same";
    MyKey key1 = key1_s.c_str();
    MyKey key2 = key2_s.c_str();

    // Make sure addresses are different.
    assert(key1 != key2);

    // Make sure hashes are identical.
    assert(MyMapHash{}(key1) == MyMapHash{}(key2));

    // Insert two values with the same key.
    MyMap map;
    map.insert({key1, 1});
    map.insert({key2, 2});

    // Make sure we find them in the map.
    auto it1 = map.find(key1);
    auto it2 = map.find(key2);
    assert(it1 != map.end());
    assert(it2 != map.end());

    // Get values.
    int value1 = it1->second;
    int value2 = it2->second;

    // The first one of any of these asserts fails. Why is there not only one
    // entry in the map?
    assert(value1 == value2);
    assert(map.size() == 1u);
}

The fingerprint in the debugger shows that the card contains two elements immediately after they are inserted.

(gdb) p map
$4 = std::unordered_map with 2 elements = {
  [0x7fffffffda20 "same"] = 2,
  [0x7fffffffda00 "same"] = 1
}

Why does this happen if a hash function that delegates a value std::hash<std::string>takes into account only its value (this is indicated in the code)?

Also, if this is the intended behavior, how can I use a card with a C string as a key, but with a key value display of 1: 1?

+4
source share
4 answers

, - (, std::unordered_map) - , . - , . , - , , -. , -.

-, . , char*, . , . , -, KeyEqual std::unordered_map.

+5

, unordered_map - , , . char , .

, KeyEqual .

struct MyKeyEqual
{
    bool operator()(MyKey const &lhs, MyKey const &rhs) const
    {
        return std::strcmp(lhs, rhs) == 0;
    }
};
+1

unordered_map -. , -. , .

, : , , C .

You can fix this by providing your own template KeyEqualimplementation to actually perform a comparison of C strings, for example by calling strcmp:

return !strcmp(lhsKey, rhsKey);
+1
source

You did not define a key map, but a map of pointers to a key.

typedef char const* const MyKey;

The compiler can optimize two instances "name"and use only one instance in the const data segment, but this can happen or not. A.K.A. undefined.

Your card must contain the key itself. Make a key a std::stringor similar.

0
source

Source: https://habr.com/ru/post/1681606/


All Articles