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

Bai*_*ang 15 c++ constructor stl map

我有以下代码:

#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;

}
Run Code Online (Sandbox Code Playgroud)

输出是:

$ 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
Run Code Online (Sandbox Code Playgroud)

谁能解释为什么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));
Run Code Online (Sandbox Code Playgroud)

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

正如6502所述,以下声明已经改变,因此不再适用:

对此函数的调用(operator [])等效于:(*((this-> insert(make_pair(x,mapped_type()))).first)).second

operator []实现不同以避免make_pair()引入额外的副本

650*_*502 12

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

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

无论如何,对于标准容器,请注意元素被认为是"值",即实现可以自由复制,并且不能保证将生成多少个副本.

您可以make_pair通过将代码更改为来查看问题

    mymap2.insert(std::pair<const Key, int>(k, 1));
Run Code Online (Sandbox Code Playgroud)

更长的解释

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

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

即使它显然是"逻辑"的,a pair<X, Y>应该可以直接使用在pair<const X, Y>预期的地方,这在C++中是不正确的,并且它是const-correctness概念的逻辑问题之一(它不能按比例缩放).