Tuesday, June 5, 2012

map insertion with value_type and make_pair

Here's a peculiarity with MSVC 10 (not tested with other compilers):

First, create a class that keeps track of copies/moves:


#include <iostream>
#include <map>
#include <vector>

class Tracker {
public:
 Tracker() { std::cout << "Default Constructor\n"; }
 ~Tracker() { std::cout << "Destructor\n"; }
 Tracker(const Tracker&) { std::cout << "Copy Constructor\n"; }
 Tracker(Tracker&&) { std::cout << "Move Constructor\n"; }
 Tracker& operator=(const Tracker&) { std::cout << "Copy Assignment\n"; return *this; }
 Tracker& operator=(Tracker&&) { std::cout << "Move Assignment\n"; return *this; }
 bool operator<(const Tracker&) const { return false; }
};


Next, let's try to insert into a map using both value_type and make_pair. Note that C++11 has an overload to handle the pair type, which is convertible to the value_type (the only difference being that in the value_type, the first element is of type const Key rather than just Key).


int main()
{
 typedef std::map<Tracker,int> counter;
 counter m;

 std::cout << "Insert with value_type:\n";
 m.insert(counter::value_type(Tracker(),1));
 std::cout << "Done\n";
 m.clear();

 std::cout << "Insert with make_pair:\n";
 m.insert(std::make_pair(Tracker(),1));
 std::cout << "Done\n";
 m.clear();
}

You'd expect that both these operations would do the same thing: move the pairs into the map. However, the output is as follow:

Insert with value_type:
Default Constructor <------- Tracker()
Move Constructor <-------- counter::value_type(Tracker(),1)
Copy Constructor <-------- inside insert, copies value_type!
Destructor
Destructor
Done
Destructor
Insert with make_pair:
Default Constructor <------- Tracker()
Move Constructor <-------- std::make_pair(Tracker(),1)
Move Constructor <-------- inside insert, moves pair!
Destructor
Destructor
Done
Destructor

In each case, the default constructor is called for Tracker(). The object is then moved into the pair.
However, insertion with value_type copies the pair, whereas make_pair moves it!
This is quite unexpected, since value_type is the intended type to be used, and should hence be optimal!

Looking at the STL headers, it seems like the rvalue reference is lost during forwarding at some point (but I'm not sure). MSVC 10 bug?

1 comment:

  1. Y u no use syntax highlighting?

    ReplyDelete