Paste some trace into FreeMemory and replace main with this:
int main(int argc, char *argv[]) { printf("map\n"); std::map<int, vec> z; printf("vec\n"); vec x; printf("pair\n"); std::pair<int,vec> y(1,x); printf("insert\n"); z.insert(y); printf("inserted 1\n"); y.first = 2; printf("insert\n"); z.insert(y); printf("inserted 2\n");
}
Output:
$ make mapinsert CXXFLAGS=-O3 -B && ./mapinsert g++ -O3 mapinsert.cpp -o mapinsert map vec pair getMem n 0 ptr 0x6b0258 insert getMem n 0 ptr 0x6b0268 getMem n 32 ptr 0x6b0278 getMem n 0 ptr 0x6b02a0 FreeMemory ptr 0x6b0268 inserted 1 insert getMem n 0 ptr 0x6b0268 getMem n 32 ptr 0x6b02b0 getMem n 0 ptr 0x6b02d8 FreeMemory ptr 0x6b0268 inserted 2 FreeMemory ptr 0x6b0258 FreeMemory ptr 0x6b02d8 FreeMemory ptr 0x6b02b0 FreeMemory ptr 0x6b02a0 FreeMemory ptr 0x6b0278
So your 3-bit distributions:
- One of them is copying an empty vector into a pair.
- One of them is to save a copy of the empty vector on the map.
These two are clearly necessary. I am not sure about this:
- One of them is to copy the vector somewhere in the call to
insert , and this is also freed up in the call to insert.
As if insert (or something that it calls inside itself) takes its parameter by value instead of a link, or insert explicitly takes a copy into an automatic variable for a while before it assigns a new node map. Dismissing the debugger is an effort for me at the moment, I will leave it to someone else.
Edit: mystery solved. insert accepts a std::pair<const int, vec> , not a std::pair<int, vec> . An additional copy of the empty vector is that the pair you create must be converted to (previously) temporary, then a link to this temporary is passed to insert . std :: pair has a constructor template that lets you get away with just about anything. 20.2.2 / 4:
template<class U, class V> pair(const pair<U,V> &p);
Effects: initializes members from the corresponding members of the argument, performing implicit conversions as needed.
I also noticed that in my implementation of vec x; does not call getMem , but vec x(0); does. So actually:
z[1] = vec();
The code is reduced and forbids you to make an additional copy (although instead it calls operator= ). It still makes 2 0-dimensional distributions, at least for me.
The C ++ standard defines operator[] to return the result of a specified expression with an insert call. I'm not sure if this means that the effects of operator[] are called “as if” make_pair and insert were called (that is, the standard is also good as to what source should be for operator[] ), or just that the return value is the same value as the specified expression. If the latter, then perhaps the implementation can do this with a single 0-size distribution. But, of course, map does not have a guaranteed way to create a record without creating a pair containing the mapped type, so 2 distributions should be expected. Or more correctly, 2 copies of the desired mapped value: the fact that copying a vector of size 0 makes a selection of size 0 depends on the implementation.
So, if you had a case where the value was really expensive to copy, but really cheap to build by default (for example, a container with a lot of elements), then the following may be useful:
std::map<int, vec> z; vec x(1000); z[1] = x;
makes 2 selections of size 4000 and 2 of size 0, whereas:
std::map<int, vec> z; vec x(1000); z.insert(std::pair<const int, vec>(2, x));
makes 3 sizes 4000 and not a single size 0. In the end, the size is large enough that additional selection in the first code is cheaper than additional copying in the second code.
It is possible that the move constructors in C ++ 0x will help with this, I'm not sure.