Home > Blockchain >  Use of `std::move` in `std::accumulate`
Use of `std::move` in `std::accumulate`

Time:01-02

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::strings, 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.

  • Related