Is there any difference between:
std::map <int,std::pair<T,T>> m;
T t1,t2;
m.emplace(1,std::make_pair(t1,t2));
and:
std::map <int,std::pair<T,T>> m;
T t1,t2;
m.emplace(1,std::move(std::make_pair(t1,t2)));
Is the std::move
redundant here? Will std::map::emplace
and perfect forwarding
take care of allocating the std::pair
directly in the std::map
?
The make_pair() function, which comes under the Standard Template Library of C++, is mainly used to construct a pair object with two elements. In other words, it is a function that creates a value pair without writing the types explicitly.
std::move() is a function used to convert an lvalue reference into the rvalue reference. Used to move the resources from a source object i.e. for efficient transfer of resources from one object to another.
Q: When should it be used? A: You should use std::move if you want to call functions that support move semantics with an argument which is not an rvalue (temporary expression).
std::move is actually just a request to move and if the type of the object has not a move constructor/assign-operator defined or generated the move operation will fall back to a copy.
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With