std::move with std::make_pair
std::make_pair(...)
and std::move(std::make_pair(...))
are both rvalue expressions (the first one is a prvalue, the second one is an xvalue). Since emplace
takes forwarding references, both are deduced as the same type, so std::move
in this case is redundant, but in a general case, a redundant std::move
can inhibit copy-elision.
m.emplace(1, std::make_pair(t1, t2));
is equivalent to:
auto&& arg = std::make_pair(t1, t2);
std::pair<const int, std::pair<T, T>> e(1, std::forward<std::pair<T, T>>(arg));
which performs the following initialization of the map element's value:
auto&& arg = std::make_pair(t1, t2);
std::pair<T, T> p(std::forward<std::pair<T, T>>(arg));
Note that this is different from:
std::pair<T, T> p(t1, t2);
The former first creates a prvalue pair (makes copies of t1
and t2
), which is then moved from (moves both the copied t1
and t2
into p
). No copy-elision takes place.
The latter uses t1
and t2
to initialize both T
s stored in the pair.
To avoid the unnecessary move resulting from the first syntax, you can instead utilize piecewise construction:
m.emplace(std::piecewise_construct
, std::forward_as_tuple(1)
, std::forward_as_tuple(t1, t2));
that will be equivalent to:
auto&& arg = std::tuple<T&, T&>(t1, t2);
std::pair<T, T> p(std::get<0>(std::forward<std::tuple<T&, T&>>(arg))
, std::get<1>(std::forward<std::tuple<T&, T&>>(arg)));
that will initialize the elements of the pair from reference members bound to original t1
and t2
.