22

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
  • 101,334
  • 104
  • 266
  • 359

6 Answers6

17

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

sbi
  • 219,715
  • 46
  • 258
  • 445
  • 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
  • Doesn't this answer need to be updated? – Antonio Dec 20 '22 at 14:09
5

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

Stuart Golodetz
  • 20,238
  • 4
  • 51
  • 80
3

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
  • 661
  • 1
  • 6
  • 13
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,419
  • 1
  • 18
  • 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
  • 69,683
  • 7
  • 133
  • 150
1

Value types must be assignable, and references are not.

Anyway you can use a tr1 reference_wrapper.

peoro
  • 25,562
  • 20
  • 98
  • 150
  • OP's talking about value type, not key type. You've got the point though. –  Nov 21 '10 at 17:59