Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use of std::move in std::accumulate

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 ?

like image 615
curiousguy12 Avatar asked Sep 10 '25 18:09

curiousguy12


2 Answers

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.

like image 87
Brian Bi Avatar answered Sep 12 '25 09:09

Brian Bi


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.

like image 31
user17732522 Avatar answered Sep 12 '25 07:09

user17732522