34

I'm trying to create a map inside a map:

typedef map<float,mytype> inner_map;
typedef map<float,inner_map> outer_map;

Will I be able to put something inside inner map, or does iterator::second returns a copy?

stl_pair.h suggests the latter:

74: _T2 second;          ///< @c second is a copy of the second object

but my test program run fine with the code like this:

it = my_map.lower_bound(3.1415);
(*it).second.insert(inner_map::value_type(2.71828,"Hello world!");

So where is the truth? Is this a copy or not?

Morse
  • 1,330
  • 2
  • 17
  • 27

4 Answers4

34

I want to add a follow up answer to this question for people using C++11 iterators...

The following code:

std::map<std::string, std::string> m({{"a","b"},{"c","d"}});
for (auto i : m)
{
    std::cout << i.first << ": " << i.second << std::endl;
}

does copy the key and value since "auto" is a value by default, not a const reference (at least that's how it behaves clang 3.1).

Additionally, the code:

std::map<std::string, std::string> m({{"a","b"},{"c","d"}});
for (const std::pair<std::string,std:string>& i : m)
{
    std::cout << i.first << ": " << i.second << std::endl;
}

also copies the key and value since the correct code should be:

std::map<std::string, std::string> m({{"a","b"},{"c","d"}});
for (const auto& i : m)
{
    std::cout << i.first << ": " << i.second << std::endl;
}

or

std::map<std::string, std::string> m({{"a","b"},{"c","d"}});
for (const std::pair<const std::string,std:string>& i : m)
{
    std::cout << i.first << ": " << i.second << std::endl;
}
Matt Gallagher
  • 14,858
  • 2
  • 41
  • 43
  • 3
    +1. Having the element be a unique_ptr will prove this clearly, as you'll be unable to compile for accessing a deleted function (copy ctor). –  Dec 14 '15 at 04:07
33

The comment in stl_pair.h is misleading in this specific case.

There will be no copy, since the map::iterator actually refers to the original data inside the map (the value_type, which itself is a pair), it’s not a copy. Thus iterator::second also refers to the original data.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 1
    This is true, one can validate it by returning something like myMap.find(myKey)->second by reference. So the documentation in stl_pair.h is not only misleading, but wrong. – xamid Oct 29 '16 at 12:18
  • 5
    @xamid Well it isn’t *wrong* because the `stl_pair.h` documentation doesn’t refer to `std::map` at all, and it behaves the way it does because `std::map::find` returns a *reference* to a `std::pair` that’s stored inside the map. So the `second` member of the pair is still a value (rather than a reference), it’s the pair itself that’s the reference. The “copy of the second element” in the documentation refers to the fact that `std::pair::second` holds a copy of whatever value it was passed in the constructor. – Konrad Rudolph Oct 30 '16 at 17:23
  • Take a look [here](http://stackoverflow.com/a/596750/3410351). Quote: `A reference is the object. It is not a pointer to the object, nor a copy of the object. It is the object.` A reference is not a copy. The element that I understand is referred to 'lives' in the map, it is not the one passed to the function that added it into the map (which might as well not exist, if emplace have been used). – xamid Oct 31 '16 at 23:02
  • @xamid I know what a reference is but you're confusing the pair itself that the map returns (= a reference) with its second member variable (= a value, not a reference). And this is a *copy* of the value that the user passed into the map. So the map itself holds a copy, inside a pair structure. – Konrad Rudolph Oct 31 '16 at 23:08
  • Apparently, you didn't really understand my comment. The element in the map is the original. It is not required that there exists or existed once another equal element, since map got an emplace method (you can pass only the constructor parameters, so that the object is first created inside of the map). And the pair structure holds a reference to this original element, not a copy. – xamid Oct 31 '16 at 23:17
  • 1
    @xamid I understood you perfectly, you're just wrong: the pair does *not* hold a reference, because `second` does not have a reference type. It's true that the map may hold a value that didn't exist previously but first of all this question/answer predates move semantics, and secondly this doesn't change the fact that the comment we're discussing here does not apply to `map` at all. – Konrad Rudolph Oct 31 '16 at 23:22
  • Alright, I just checked it inside of the code. It does not hold a reference, it points to the original element that lives in the pair structure. Now this still has nothing to do with a copy of anything. They just call the original "copy", which is wrong. – xamid Oct 31 '16 at 23:27
4

The value_type a map is a pair and therefore it has members first and second. As with all iterators, a map iterator is a pseudo-pointer, i.e. it points to data within a collection and not copies of that data.

It is almost certain internally to contain pointers rather than references due to the fact that iterators can be re-assigned (that is what you use them for) and you cannot reassign references to refer to other objects.

Even if you have a const_iterator and the type underneath is POD, it must have a pointer to it, in case someone does this:

map< int, int > m;
m.insert( make_pair( 1, 2 );
map<int,int>::const_iterator citer = m.begin();
map<int,int>::iterator iter = m.begin();
iter->second = 3;
std::cout << citer->second << '\n'; // should always print 3

The behaviour should be defined and should output 3, which would not happen if the const_iterator decided to "optimise" after all it's const and only int...

CashCow
  • 30,981
  • 5
  • 61
  • 92
0

Iterators, when dereferenced, give you a reference.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055