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
?
CodePudding user response:
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
.
CodePudding user response:
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.
CodePudding user response:
You don’t show what the macros expand to, but I was just reminded, when I wrote out an example of bignum addition yesterday as a lesson, why this is more efficient.
The binary
operator creates and returns a temporary object. With operands that fit into a machine register, this can be zero-cost (at most spilling what was in the destination register before onto the stack), but sometimes, a large data structure will need to be created. And this seems to be template code that might have to implement that use case.
If the left operand can be clobbered, though,
can be implemented as =
, overwriting the operand and optimizing away the creation of a new copy.
This coding style strikes me as a bit odd—as I mentioned, =
has exactly the semantics the programmer seems to want here, so I’m not sure why they don’t use that instead.