调用std :: map operator []或insert时会发生什么

时间:2013-06-22 06:29:08

标签: c++ map stl constructor

我有以下代码:

#include <functional>   // std::less
#include <map>
#include <iostream>
using namespace std;

class Key
{
public:
        Key() {cout << "Key Constructor" << endl;}
        ~Key() {cout << "Key Destructor" << endl;}
        Key(const Key& key) {cout << "Key Copy Constructor" << endl;}

        bool operator < (const Key& k1) {return true;}
};
int main()
{
        map<Key, int> mymap;
        Key k;

        cout << "operator[]"<<endl;
        mymap[k] = 1;

        map<Key, int> mymap2;
        cout << "insert"<<endl;
        mymap2.insert(std::make_pair(k, 1));
        cout << "=========" << endl;

}

输出是:

$ g++ test.cpp -fpermissive
$ ./a.out
Key Constructor
operator[]
Key Copy Constructor
Key Copy Constructor
Key Destructor
insert
Key Copy Constructor
Key Copy Constructor
Key Copy Constructor
Key Copy Constructor
Key Destructor
Key Destructor
Key Destructor
=========
Key Destructor
Key Destructor
Key Destructor

有人可以解释为什么mymap [k] = 1;调用2个复制构造函数和mymap2.insert(std :: make_pair(k,1));调用4个复制构造函数?这是否意味着operator []比插入更有效?

感谢。

要点:

感谢用户6502和petersohn的深刻见解,我现在猜测插入2个额外复制构造函数的原因如下:

  • make_pair是一个函数,它首先在函数内部复制,然后返回副本 - 这是一个额外的副本
  • make_pair(k,1)会创建pair<Key, int>,但所需的value_typepair<const& Key, int>,类型转换会导致另一个额外的副本

所以在案例2中,如果我使用:

mymap2.insert(std::pair<const Key, int>(k, 1));

调用复制构造函数的数量与operator []

相同

正如6502所述,claim之后的情况已经改变,因此不再适用:

  

对此函数的调用(operator [])相当于:   (*((这 - &GT;插入(make_pair(X,mapped_type())))第一))。第二

operator []的实现方式不同,以避免make_pair()

引入额外的副本

2 个答案:

答案 0 :(得分:12)

insert的问题是make_pair会创建一对错误的类型,因此传递的对象需要转换为正确的对类型才能传递给insert

实际上在以前的版本中,C ++标准要求map::operator[]表现为insert(从而迫使实施效率低下)。后来文本放宽了,可以更好地实现。

请注意,对于标准容器,元素被认为是“值”,即实现可以自由复制,并且不能保证将制作多少副本。

您可以通过将代码更改为

来查看make_pair问题
    mymap2.insert(std::pair<const Key, int>(k, 1));

更长的解释

问题是,make_pair会创建std::pair<Key, int>值但insert签名需要const std::pair<const Key, int>&(请注意std::map::value_type是一对const {1}}第一个元素)。

这两种类型是不兼容和无关的,因此为了能够进行调用,必须创建另一对,同时复制密钥和值,这是额外的密钥重复发生的地方。

即使pair<X, Y>显然是“逻辑”的,pair<const X, Y>应该可以直接使用{{1}},但在C ++中却不是这样,这是const-correctness概念的逻辑问题之一(它不按比例缩放。)

答案 1 :(得分:2)

这是因为make_pair。您必须将k复制到该对中,此外还有一个额外的函数调用。可能通过启用优化来减少副本数量(我没试过)。但是,如果你这样做,那么你将拥有与使用operator []:

完全相同的副本数量
mymap2.insert(std::pair<const Key&, int>(k, 1));

但是在C ++ 11中,事情开始变得更好。如果您使用C ++ 11编译代码,那么您将获得带有插入的两个副本。更好的是,如果Key有一个移动构造函数,你将获得一个副本和一个用于插入的移动(而两个副本用operator [])。如果你在C ++ 11中使用我的上一行,你甚至可以省去移动,只获得一份副本。

有趣的是,对于运营商[],我总是得到两份,我不知道具体原因。