17

Is it possible to use a reference as the value in a standard map container in C++?
If not - why not?

Example declaration:

map<int, SomeStruct&> map_num_to_struct;

Example usage:

...
SomeStruct* some_struct = new SomeStruct();
map_num_to_struct[3] = *some_struct;
map_num_to_struct[3].some_field = 14.3;
cout<<some_struct.some_field;
...

I would expect to see 14.3 printed...

Jonathan Livni
  • 93,232
  • 99
  • 253
  • 352

6 Answers6

16

No. STL container value types need to be assignable. References are not assignable. (You cannot assign them a different object to reference.)

sbi
  • 212,637
  • 45
  • 247
  • 432
  • 2
    Since when do container value types need to be assignable? They need to be copyable. A reference is indeed not "copyable",but your answer is technically wrong. What am I missing? – Narek Apr 17 '13 at 13:50
5

No, it's not. You can use pointers as the value type, though.

Stuart Golodetz
  • 19,702
  • 4
  • 49
  • 80
2

I don't think so, references are supposed to be treated like constant pointers to a certain element if I remember correctly. But you could just use pointers to the same effect.

rtpg
  • 2,411
  • 1
  • 17
  • 31
  • It's just no fun de-referencing all your iterator values :( – GWW Nov 21 '10 at 17:51
  • 2
    but then you get to use -> instead of . and it looks cooler (this is definitely a legitimate reason to use pointers) – rtpg Nov 21 '10 at 17:52
  • @GWW, no problem - you can use `boost::inderect_iterator` to get "auto-dereferencing". – Kirill V. Lyadvinsky Nov 21 '10 at 17:53
  • @Kirill that's actually quite useful. I need to stop being lazy and start looking at boost more often. I already use it for a bunch of stuff. @Dasuraga if you are using a vector instead of a map it gets ugly `(*it)->do_something();` is not so cool – GWW Nov 21 '10 at 17:57
2

No, you can't use references but you can use pointers. You seem to be mixing up both in your example. Try:

map<int, SomeStruct *> map_num_to_struct;
SomeStruct* some_struct = new SomeStruct();
map_num_to_struct[3] = some_struct;
map_num_to_struct[3]->some_field = 14.3;
cout<<some_struct->some_field;
casablanca
  • 68,094
  • 7
  • 131
  • 148
2

I believe it is possible, with constraints. Since references aren't assignable at some later stage, you won't be able to call operator[] on the map. However, you can call various other member functions. As long as you don't break any rules for references. For example:

// You need the instances to exist before
auto a1 = SomeStruct();
auto a2 = SomeStruct();
auto a3 = SomeStruct();

// Creating the map with an initializer list.
std::map<int, SomeStruct&> map_num_to_struct = {
    { 1, a1 },
    { 2, a2 },
    { 5, a3 }
};

// The following won't work because operator[] returns
// a reference to the value, which can't be re-assigned.
// map_num_to_struct[6] = a1;

// These will work.
map_num_to_struct.insert({6, a1});
map_num_to_struct.insert(std::pair<int, SomeStruct&>(7, a1));

// Iterating through the map.
for (auto &a: map_num_to_struct) {
    cout << a.first << ": " << a.second.some_field << endl;
}

// We can't use operator[] for indexing.
// map_num_to_struct[5].do_something();
auto a_iter = map_num_to_struct.find(5);
if (a_iter != map_num_to_struct.end()) {
    cout << a_iter->first << ": " << a_iter->second.some_field << endl;
    a_iter->second.some_field = 14.3;
    cout << a_iter->first << ": " << a_iter->second.some_field << endl;
}

I don't know if the newer C++ standards have made this possible, but it works with GCC and clang at least.

benzeno
  • 641
  • 1
  • 6
  • 13
1

Value types must be assignable, and references are not.

Anyway you can use a tr1 reference_wrapper.

peoro
  • 24,831
  • 19
  • 94
  • 148
  • OP's talking about value type, not key type. You've got the point though. –  Nov 21 '10 at 17:59