Я думаю, что это правильно.
RVO
Я думаю, что RVO должно работать нормально, если оно реализовано в вашем компиляторе , потому что bar()
возвращает временный объект, и компилятор знает, какой объект должен быть возвращен из bar()
.Кроме того, в C ++ 17 и более в этом случае гарантируется RVO.
Применимо ли RVO (оптимизация возвращаемого значения) ко всем объектам?
Что такое оптимизация копирования и возврата значения?
emplace vs. insert ( DEMO )
std :: map ::emplace
In
my_map.emplace("key", bar());
, RVO заменяет bar()
на std::make_shared<Foo>()
и создается std::shared_ptr<Foo>
.Затем он пересылается и move-ctor из std::shared_ptr<Foo>
называется только один раз при построении на месте нового элемента my_map
.
std :: map :: insert с std :: make_pair
В
my_map.insert(make_pair("key", bar()));
, RVO снова заменяет bar()
на std::make_shared<Foo>()
.
Далеес 20.3.2 из N3337 , C ++ 11 с исправленными незначительными ошибками, шаблон класса std::pair
равен
namespace std{
template<class T1, class T2>
struct pair
{
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
...
template<class U, class V> pair(U&& x, V&& y);
...
}
}
, где шаблон ctor равен
template<class U, class V> pair(U&& x, V&& y);
Эффекты : конструктор инициализирует first
с std::forward<U>(x)
и second
с std::forward<V>(y)
.
Кроме того, с 20.3.3
template<class T1, class T2>
pair<V1, V2> make_pair(T1&& x, T2&& y);
Возвращает : pair<V1, V2>(std::forward<T1>(x), std::forward<T2>(y))
; ...
Таким образом, типичная реализация std::make_pair
для этого определения рассматривается так:
namespace std{
template<class T1, class T2>
inline pair<typename decay<T1>::type, typename decay<T2>::type>
make_pair(T1&& x, T2&& y)
{
return pair<typename decay<T1>::type,
typename decay<T2>::type>
(std::forward<T1>(x), std::forward<T2>(y));
}
}
и RVO снова будет работать для std::make_pair
.Фактически, число вызовов move-ctor увеличивается на единицу, если мы скомпилируем DEMO с C ++ 14 и опцией компилятора "-fno-elide-constructors", которая отключает RVO для g ++.
Поэтому в std::make_pair("key", bar())
я ожидаю следующее senario:
- 1) RVO заменяет
bar()
на std::make_shared<Foo>()
, - 2) RVO также заменяет
std::make_pair
с std::pair::pair
, - 3)
std::shared_prtr<Foo>
создается и затем пересылается на std::pair::pair
, - 4)
std::shared_prtr<Foo>
создается как элемент second
с помощью move-ctor .
Наконец, std::map::insert
также перегружен для значений r:
// since C++11
template< class P >
std::pair<iterator,bool> insert( P&& value );
Здесь снова
- 5) move-ctor из
std::shared_prtr<Foo>
вызывается.
В итоге я ожидаю, что
my_map.insert(make_pair("key", bar()));
вызывает move-ctor из std::shared_ptr<Foo>
дважды .
Мой ответ
Поскольку в обоих случаях копии std::shared_ptr<Foo>
не делаются вообще и почти одинаковое количество ходовназываются, их производительность будет почти такой же.
Перемещение объектаt в карту
вставка vs emplace vs оператор [] в карту c ++
Является ли оператор c ++ 11 [] эквивалентным emplace onвставка карты?
Примечание 1, универсальная ссылка
В C ++ 11 и более поздних, левый и правый аргумент std::make_pair
- это то, что Скотт Мейерс называет универсальная ссылка , то есть они могут принимать и l-значение, и r-значение:
// until C++11
template< class T1, class T2 >
std::pair<T1,T2> make_pair( T1 t, T2 u );
// since C++11
template< class T1, class T2 >
std::pair<V1,V2> make_pair( T1&& t, T2&& u );
Таким образом, если мы передаем l-значение std::make_pair
, то copy-ctor std::shared_ptr<Foo>
вызывается в результате универсальной ссылки.
Примечание 2, std :: map :: try_emplace
std::map::emplace
всегда создает новый элемент my_map
, даже еслиключ уже существует.Если ключ уже существует, эти новые экземпляры уничтожаются.Но начиная с C ++ 17, мы получаем
template <class... Args>
pair<iterator, bool> try_emplace(key_type&& k, Args&&... args);
Эта функция не создает args
, если ключ k
уже существует в контейнере.Хотя мы уже знаем, что в данной проблеме нет повторяющегося ключа, если мы не знаем, существует ли дублирующий ключ, этот std::map::try_emplace
предпочтительнее.
Есть лиесть ли причина использовать std :: map :: emplace () вместо try_emplace () в C ++ 1z?