In my Fedora 34 environment (g++), std::accumulate
is defined as:
template<typename ITER, typename T>
constexpr inline T accumulate(ITER first, ITER last, T init)
{
for (; first != last; ++first)
init = std::move(init) + *first; // why move ?
return init;
}
If the expression init + *first
is already an rvalue, what is the purpose of std::move
?
std::move(init) + *first
can sometimes generate more efficient code than init + *first
, because it allows init
to be overwritten. However, since (as you observed) the result of the +
will generally be an rvalue, there is no need to wrap the entire expression in a second std::move
.
For example, if you are accumulating std::string
s, then std::move(init) + *first
might be able to append *first
into reserved-but-not-yet-used space in init
's buffer instead of having to allocate a new buffer whose length is the sum of the lengths of init
and *first
.
The value category of init + *first
doesn't matter.
init
in init + *first
is a lvalue.
So if init + *first
calls an operator+
overload taking the parameter by-value, it will cause a copy construction of that parameter
But the value of init
is not required anymore after init + *first
, so it makes sense to move it into the parameter instead.
Similarly a operator+
overload taking its first argument by rvalue-reference might be used to allow modification of the argument by the operation.
This is what std::move
achieves here.
The standard specifies this behavior since C++20.
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