Ложные копии в C++03 libstdc++ vs c++11
рассмотрим этот код:
#include <iostream>
#include <string>
#include <map>
using namespace std;
class Foo
{
public:
Foo() : _x(0)
{
cout << "Default" << endl;
}
Foo(int a) : _x(a)
{
cout << "Param" << endl;
}
Foo(Foo const &foo) :
_x(foo._x)
{
cout << "Copy" << endl;
}
Foo& operator=(Foo const &foo)
{
cout << "Assignment" << endl;
_x = foo._x;
return *this;
}
int get(void)
{
return _x;
}
private:
int _x;
};
int main(int argc, char *argv [])
{
std::map<int, Foo> foos;
Foo a_foo(10);
foos[100] = a_foo;
return 0;
}
скомпилирован в gcc с -std=c++11, и вы получаете вывод,
Param
Default
Assignment
Remove-std=c++11, затем вы получаете,
Param
Default
Copy
Copy
Assignment
libc++ пример создания превосходного вывода в режиме c++03
откуда берутся две дополнительные копии?
они связаны с вызовом оператора subscript, не задание. (Они останутся, если вы удалите назначение.) Мне кажется, что они не нужны, даже в мире до c++11, как показывает пример libc++.
Это было первоначально мотивировано глядя на этот вопрос
1 ответов
это LWG 334:
стандарт C++03 предписывает следующие эффекты для operator[] ([lib.карта.access]p1):
возвращает:
(*((insert(make_pair(x, T()))).first)).second.
libstdc++ реализует вставку, используемую operator[] (в случае, если ключ еще не существует) в режиме C++03:
__i = insert(__i, value_type(__k, mapped_type()));
__i является точкой вставки, она вычисляется как
iterator __i = lower_bound(__k);
__k является параметром operator[].
создание временного value_type(__k, mapped_type()) вызывает первую копию (от mapped_type() на value_type пара). Вторая копия является результатом insert, которая копирует value_type сопряжение в фактический узел.
первоначальная версия от 1997 является следующим:
return (*((insert(value_type(k, T()))).first)).second;
что почти соответствует букве стандарта (которого тогда даже не существовало!). Последний раз он был существенно изменен в 1998 году. До этого используется:
__i = insert(__i, value_type(__k, _Tp()));
в сообщении фиксации говорится, что это было
обновление до SGI STL 3.11.
более ранние версии SGI STL (1995) действительно указать map::operator[] таким же образом, как стандарт C++03:
на карте
mи клавишуk,m[k]семантически эквивалентно(*((m.insert(make_pair(k, T()))).first)).second.
SGI STL v2.03 (1997) уже переключился на использование value_type вместо make_pair. И, как предполагает журнал фиксации gcc, реализация SGI STL снова изменилась между v3.0 (также 1997) и v3.11 (1998) from insert(value_type(.. к форме, все еще присутствующей в libstdc++, используя lower_bound и только создание пары, если ключ еще не существует.
таким образом, можно сказать, что libstdc++ реализует первое предлагаемое разрешение LWG 334 (value_type вместо make_pair). Однако, глядя на его историю, это не совсем то, что произошло. Это просто после SGI STL. libc++ строго не соответствует C++03 в этом отношении.
версия C++11 libstdc++того же оператора использует пользовательскую функцию emplacement. Спецификация стандарта C++11 map::operator[] следует предложенной резолюции РГПВ 334:
эффекты: если нет ключа эквивалентно
xна карте, вставляетvalue_type(x, T())на карте.
(где x - это параметр operator[])