Ложные копии в 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[]
)