15

我有以下代码:

#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[] 比 insert 效率更高?

谢谢。

概括:

感谢用户 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 所指出的,以下声明已更改,因此不再正确:

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

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

4

2 回答 2

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 签名需要 a const std::pair<const Key, int>&(注意这std::map::value_type是一个带有const第一个元素的对)。

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

即使 apair<X, Y>应该在pair<const X, Y>预期的地方直接可用显然是“合乎逻辑的”,但这在 C++ 中是不正确的,它是 const 正确性概念的逻辑问题之一(它不会按组合进行扩展)。

于 2013-06-22T06:38:34.600 回答
2

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

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

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

有趣的是,使用 operator[],我总是得到两份副本,我不知道确切的原因。

于 2013-06-22T06:45:40.580 回答