Что произошло при вызове std:: map operator [] или вставить - программирование
Подтвердить что ты не робот

Что произошло при вызове std:: map operator [] или вставить

У меня есть следующий код:

#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; invoke 2 copy constructor и mymap2.insert(std:: make_pair (k, 1)); вызывает 4 конструктора копирования? и означает ли это, что оператор [] намного эффективнее, чем вставка?

Спасибо.

Резюме:

Спасибо пользователю 6502 и petersohn за ваше понимание. Теперь я предполагаю, что причина для 2 дополнительных конструкторов копий для вставки такова:

  • make_pair - это функция, она сначала выполняет функцию копирования внутри функции, а затем возвращает копию - это одна дополнительная копия
  • make_pair (k, 1) создаст pair<Key, int>, но требуемый value_type будет pair<const& Key, int>, преобразование типа приведет к другому дополнительная копия

Итак, в случае 2, если я использую:

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

Число вызываемых конструкторов копий будет одинаковым с оператором []

Как отмечалось в 6502 году, после претензии было изменено, следовательно, это не так:

Вызов этой функции (оператор []) эквивалентен: (* ((Этом- > вставки (make_pair (х, mapped_type()))). Первый)). Вторая

operator [] реализован иначе, чтобы избежать дополнительной копии, введенной make_pair()

4b9b3361

Ответ 1

Проблема со вставкой заключается в том, что make_pair создаст пару неправильного типа, чтобы переданный объект нуждался в преобразовании в соответствующий тип пары, который должен быть передан в insert.

На самом деле в прежней версии стандарт С++ обязал 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).

Эти два типа несовместимы и не связаны друг с другом, поэтому для того, чтобы сделать вызов, необходимо создать еще одну пару, копируя как ключ, так и значение, и здесь происходит дополнительное дублирование ключей.

Даже если это может быть, по-видимому, "логично", что a pair<X, Y> должно быть непосредственно использовано, где pair<const X, Y> ожидается, что это не так в С++, и это одна из логических проблем концепции const-correctness (это не " t по составу).

Ответ 2

Это из-за make_pair. Вы должны скопировать k в пару, плюс есть дополнительный вызов функции. Возможно, количество копий можно уменьшить, включив оптимизацию (я не пытался). Однако, если вы это сделаете, то у вас будет точно такое же количество копий, что и у оператора []:

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

В С++ 11 все становится лучше. Если вы скомпилируете свой код с С++ 11, вы получите две копии со вставкой. Еще лучше, если Key имеет конструктор перемещения, вы получите одну копию и один шаг для вставки (в то время как две копии с оператором []). Если вы используете мою линию выше в С++ 11, вы даже избавитесь от перемещения и получите только одну копию.

Интересно, что с оператором [] я всегда получаю две копии, для которых я не знаю точной причины.