Alok Singhal
Alok Singhal

Reputation: 96141

gnu C++ hash_map: erasing a key: defined?

Consider the following program, which is a minimal example trying to reproduce a problem with some legacy code:

#include <iostream>
#include <ext/hash_map>

// Define a hash for std::string class so we can use it as keys
// in hash_map below.
namespace __gnu_cxx {
    template <>
        struct hash<std::string> {
            size_t operator() (const std::string& x) const {
                return hash<const char*>()(x.c_str());
            }
        };
}

// Data class contains a string
class Data {
    public:
        std::string s;
        Data() { s = "foobar"; }
        Data(std::string s_) : s(s_) {}
};

// Map keyed by string.  Values are Data instances
typedef __gnu_cxx::hash_map<std::string, Data> DataMap;

int main()
{
    DataMap m;
    std::string key = "test";

    // I am storing a "Data" instance d, for "key".  d.s is the same as key.
    Data d = Data(key);
    m[key] = d;

    DataMap::iterator it = m.find(key);
    if (it == m.end()) {
        std::cerr << "not there " << std::endl;
        return 1;
    }
    Data *dp = &it->second;
    // Question about the following line.  Is the behavior well-defined?
    m.erase(dp->s);

    return 0;
}

I am storing my class Data instances in a hash_map. I search for a particular data member using a key, and then erase that value using m.erase(dp->s). m.erase(dp->s) will delete the object pointed to by dp. Am I allowed to use dp->s in the call to erase(), or must I first make a copy and then erase():

std::string key_to_delete = dp->s;
m.erase(key_to_delete);

Upvotes: 1

Views: 1734

Answers (1)

Anthony
Anthony

Reputation: 12397

Looking at the implementation, it seems as if even after the node (the pair pointed to by it) is deleted, the key passed to the erase function is still referenced. If dp is deleted, then the reference to dp->s becomes invalid. Yet the implementation of hash_map is still trying to dereference it. Fail.

You would need to pass something that is guaranteed to stay valid for the call to erase.

You could

m.erase(key);

Or you can use the iterator returned by find to do the erase:

m.erase(it);

Upvotes: 1

Related Questions