简体   繁体   中英

Why does the map.insert() method invoke the copy constructor twice?

I'm creating the custom class Node in order to implement a binary tree using a map<int,Node> container: the int key of the map is the identifier of a Node object. In the class Node I had to implement a copy constructor.

When inserting a Node object on the map, I noticed that the copy constructor of the Node is invoked twice. Why?

cout << "node2" << endl;
Node node2;
node2.set_depth(2);
node2.make_it_branch(3,4);

cout << "map" << endl;
map<int,Node> mapping;
cout << "toInsert" << endl;
pair<int,Node> toInsert = pair<int,Node>(2,node2);
cout << "insert" << endl;
mapping.insert(toInsert);

Running the above code, the output is as follows:

node2
--- Node()
map
toInsert
--- Node(const Node& orig)
insert
--- Node(const Node& orig)   // Why does the copy constructor be invoked twice?
--- Node(const Node& orig)   // ------------------------------------------------
--- ~Node()
--- ~Node()
--- ~Node()
--- ~Node()

Most likely because the value type of your map is pair<int const, Node> , not pair<int, Node> : in a map, the key is constant .

Since insert() accepts a pair<int const, Node> const& and you supply a pair<int, Node> , to perform the conversion a temporary must be constructed from which the value in the map can in turn be copy-constructed.

To verify it, change this line:

pair<int, Node> toInsert = pair<int, Node>(2, node2);

Into this line:

pair<int const, Node> toInsert = pair<int const, Node>(2, node2);

And you should see the extra call to the copy constructor disappear.

Also keep in mind, that the concrete implementation of Standard Library containers are not required to perform a particular number of copies: implementations may vary, and different optimization levels could make things different as well.

You are using pair<int,Node> . The type taken by the insert method is map<K,V>::value_type which is defined as pair<const K,V> . The compiler must insert an additional copy to convert between these two types.

Try using map<int,Node>::value_type instead of pair<int,Node> . It is better to use the types defined by the class itself rather than recreating them from scratch.

You can also avoid your first copy by writing.

map<int,Node>::value_type toInsert(2,node2);

instead of

map<int,Node>::value_type toInsert = map<int,Node>::value_type(2,node2);

When you do the following:

toInsert = pair<int, Node>(2, node2);

You are passing the node2 into the constructor of the pair object. Even though you are passing by reference, conceptually you are binding the values together, and that means the pair object is making a copy of the node2 object. Copy #1.

When you pass this pair object to the insert function:

mapping.insert(toInsert);

.. yes you are passing by reference, but the container doesn't know anything about the lifetime of the object referenced ( toInsert ). So it makes it's own copy to store in the container. Copy #2.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM