1

I am creating an unordered_map (C++ STL). The key is of type std::string and the value will be a pointer to objects of class X. the string key is actually the name of the object itself and will be stored as an instance variable in objects of that class. Is there a way for me to insert key,value pairs in unordered_map so that it does not allocate memory for the key?

I came up with the following solution ->

class X
{
public:
    const string name;
    X(char * c_name) : name(c_name) {}
};

unordered_map<string, X *> x_store;
X *a = new X("some_name"); 
x_store.insert(make_pair(a -> name, a))

but i believe the string object will be duplicated.

Aviral Goel
  • 288
  • 1
  • 3
  • 13
  • 2
    No its not possible, because at least the map must allocate memory to hold a pointer to something, when you would use a pointer to string for example. – user743414 Jul 01 '14 at 11:59
  • @user743414 I want the map to store a reference to the string object instead of creating a copy, because the string object is already present in mapped value pointers. – Aviral Goel Jul 01 '14 at 12:05
  • You can't store references in STL containers. – 101010 Jul 01 '14 at 12:06
  • 2
    You probably want a `std::set` with a suitable comparator. – Kerrek SB Jul 01 '14 at 12:08
  • @40two So is it better for me to create the string object separately and make the objects of class X point to those string objects (through pointers or reference) and use the string objects themselves as keys? – Aviral Goel Jul 01 '14 at 12:08
  • No follow @KerrekSB 's advice. – 101010 Jul 01 '14 at 12:13

1 Answers1

3

You cannot make unordered_map rely on your keys: it must store its own copy, because you could potentially change the string inside your X class.

However, you do not need to use a map - it looks like an unordered_set would be sufficient in your situation, with some custom equality and hash functions:

auto my_hash = [](X const& x) {
    return std::hash<std::string>()(x.name);
};
auto my_eq = [](X const& x, X const& y) {
    return std::equal_to<std::string>()(x.name, y.name);
};
std::unordered_set<X,my_hash,my_eq> mySet;

Now the key information is stored within the X object, which is stored inside mySet. Of course you cannot query a set by a string key, but you can use find and a "query object" to achieve the same result:

X query("abc"); // Set the name to "abc"
X *current = mySet.find(query);
if (current) {
    cout << current->name << endl; // Will print "abc"
} else {
    cout << "Not found" << endl;
}
Sergey Kalinichenko
  • 675,664
  • 71
  • 998
  • 1,399
  • the key is declared as a const instance variable so there is no way i can change it once the object is created. – Aviral Goel Jul 01 '14 at 12:17
  • @AviralGoel `unordered_map` has no way of knowing that, though, so it must assume that the key that you have passed it is changeable, and make a copy to stay on the defensive side. – Sergey Kalinichenko Jul 01 '14 at 12:19
  • The code you provided does not work with g++ (Ubuntu/Linaro 4.7.3-1ubuntu1) 4.7.3. I believe my_hash and my_eq are supposed to be functors. Is there something I have to do to make your lambda function version work? – Aviral Goel Jul 01 '14 at 12:53
  • @AviralGoel Right, this is the C++11 code with lambdas. You need to compile with `-std=C++11` to make it work. You can also make these two functions as well (see [this answer](http://stackoverflow.com/a/15869103/335858) for an explanation on how this can be done). – Sergey Kalinichenko Jul 01 '14 at 13:00
  • You don't want to instantiate the `unordered_set` on the variables; you need something like `decltype`, to get the type of the variables. (When all is said and done, it's probably easier to just define the functional object directly than to use lambdas and `auto`.) – James Kanze Jul 01 '14 at 14:22
  • And really: shouldn't `std::unordered_set::find` (and `std::set::find`) be templates, with the requirement that the type given be compatible with the hash/equality predicate (ordering for `std::set::find`), along the lines of what `std::lower_bound` does. Obviously, you'd need explicit types, rather than lambda/`auto`, since they'd have to overload `operator()(...)` on all of the combinations of types you want to use. But I regularly do this with `std::lower_bound`; there's no reason this should be different. – James Kanze Jul 01 '14 at 14:28