std::maps within std::maps -- optimisation

S

Simon Elliott

Here's a small example which uses std::maps within std::maps. Note the
line flagged // *** copy?

#include <iostream>
#include <map>
#include <string>

struct Tfoo
{
Tfoo():i1_(0),i2_(0){}
Tfoo(int i1, int i2):i1_(i1),i2_(i2){}
int i1_;
int i2_;
};

typedef std::map<std::string, Tfoo> TfooMapInner;
typedef std::map<std::string, TfooMapInner> TfooMapOuter;

int main(int argc, char **argv)
{
TfooMapOuter myFooMapOuter;

{
TfooMapInner myFooMapInner;
myFooMapInner["inner_key_1"] = Tfoo(0,1);
myFooMapInner["inner_key_2"] = Tfoo(0,2);
myFooMapOuter["outer_key_1"] = myFooMapInner;
}

{
TfooMapInner myFooMapInner;
myFooMapInner["inner_key_3"] = Tfoo(0,3);
myFooMapInner["inner_key_4"] = Tfoo(0,4);
myFooMapOuter["outer_key_2"] = myFooMapInner;
}

TfooMapOuter::const_iterator ipOuter;
TfooMapOuter::const_iterator epOuter = myFooMapOuter.end();
for (ipOuter=myFooMapOuter.begin(); ipOuter!=epOuter; ++ipOuter)
{
const std::string& outerKey = (*ipOuter).first;
const TfooMapInner& fooMapInner = (*ipOuter).second; // *** copy?
TfooMapInner::const_iterator ipInner;
TfooMapInner::const_iterator epInner = fooMapInner.end();
for (ipInner=fooMapInner.begin(); ipInner!=epInner; ++ipInner)
{
const std::string& innerKey = (*ipInner).first;
const Tfoo& foo = (*ipInner).second;
cout << outerKey << " " << innerKey << " " << foo.i1_ << " "
<< foo.i2_ << std::endl;
}
}

return 0;
}

This produces the output I expected:

outer_key_1 inner_key_1 0 1
outer_key_1 inner_key_2 0 2
outer_key_2 inner_key_3 0 3
outer_key_2 inner_key_4 0 4

But I'm concerned about the line I flagged (// *** copy?)
const TfooMapInner& fooMapInner = (*ipOuter).second;

Is (*ipOuter).second a copy of the TfooMapInner? If so, how can I avoid
this unnecessary copy being made?
 
R

Rolf Magnus

Simon Elliott said:
Here's a small example which uses std::maps within std::maps. Note the
line flagged // *** copy?

#include <iostream>
#include <map>
#include <string>

struct Tfoo
{
Tfoo():i1_(0),i2_(0){}
Tfoo(int i1, int i2):i1_(i1),i2_(i2){}
int i1_;
int i2_;
};

typedef std::map<std::string, Tfoo> TfooMapInner;
typedef std::map<std::string, TfooMapInner> TfooMapOuter;

int main(int argc, char **argv)
{
TfooMapOuter myFooMapOuter;

{
TfooMapInner myFooMapInner;
myFooMapInner["inner_key_1"] = Tfoo(0,1);
myFooMapInner["inner_key_2"] = Tfoo(0,2);
myFooMapOuter["outer_key_1"] = myFooMapInner;
}

{
TfooMapInner myFooMapInner;
myFooMapInner["inner_key_3"] = Tfoo(0,3);
myFooMapInner["inner_key_4"] = Tfoo(0,4);
myFooMapOuter["outer_key_2"] = myFooMapInner;
}

TfooMapOuter::const_iterator ipOuter;
TfooMapOuter::const_iterator epOuter = myFooMapOuter.end();
for (ipOuter=myFooMapOuter.begin(); ipOuter!=epOuter; ++ipOuter)
{
const std::string& outerKey = (*ipOuter).first;
const TfooMapInner& fooMapInner = (*ipOuter).second; // *** copy?
TfooMapInner::const_iterator ipInner;
TfooMapInner::const_iterator epInner = fooMapInner.end();
for (ipInner=fooMapInner.begin(); ipInner!=epInner; ++ipInner)
{
const std::string& innerKey = (*ipInner).first;
const Tfoo& foo = (*ipInner).second;
cout << outerKey << " " << innerKey << " " << foo.i1_ << " "
<< foo.i2_ << std::endl;
}
}

return 0;
}

This produces the output I expected:

outer_key_1 inner_key_1 0 1
outer_key_1 inner_key_2 0 2
outer_key_2 inner_key_3 0 3
outer_key_2 inner_key_4 0 4

But I'm concerned about the line I flagged (// *** copy?)
const TfooMapInner& fooMapInner = (*ipOuter).second;

Is (*ipOuter).second a copy of the TfooMapInner? If so, how can I avoid
this unnecessary copy being made?

First insert the map, then fill it instead of the other way round:

TfooMapInner& myFooMapInner = myFooMapOuter["outer_key_2"];
myFooMapInner["inner_key_3"] = Tfoo(0,3);
myFooMapInner["inner_key_4"] = Tfoo(0,4);
 
S

simont

Dereferencing (*ipOuter).second gives you a reference to the inner map
instance which already exists in the outer map. Initialising a
reference from that (as you do) just gives it a name.

The only place a map is copied is when you're setting up (see Rolf's
suggestion for avoiding that). You aren't copying anything in the bit
you flagged.
 
S

Simon Elliott

Is (*ipOuter).second a copy of the TfooMapInner? If so, how can I
avoid this unnecessary copy being made?

First insert the map, then fill it instead of the other way round:

TfooMapInner& myFooMapInner = myFooMapOuter["outer_key_2"];
myFooMapInner["inner_key_3"] = Tfoo(0,3);
myFooMapInner["inner_key_4"] = Tfoo(0,4);

Thanks - that makes sense.
 
S

Simon Elliott

Dereferencing (*ipOuter).second gives you a reference to the inner map
instance which already exists in the outer map. Initialising a
reference from that (as you do) just gives it a name.

I take your point about the reference, but I was concerned that a
std::pair might be created on the fly by the iterator. But now I come
to think about it, this wouldn't be much use if you wanted to use the
iterator to change the map data!
The only place a map is copied is when you're setting up (see Rolf's
suggestion for avoiding that). You aren't copying anything in the bit
you flagged.

That's useful to know.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,825
Latest member
VernonQuy6

Latest Threads

Top