Что произошло при вызове 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()
Ответы
Ответ 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, вы даже избавитесь от перемещения и получите только одну копию.
Интересно, что с оператором [] я всегда получаю две копии, для которых я не знаю точной причины.