cur*_*y12 26 c++ move-semantics stdmove
在我的 Fedora 34 环境(g++)中,std::accumulate定义为:
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;
}
Run Code Online (Sandbox Code Playgroud)
如果表达式init + *first已经是右值,那么 的目的是什么std::move?
Bri*_*ian 25
std::move(init) + *first有时可以生成比 更有效的代码init + *first,因为它允许init被覆盖。但是,由于(正如您所观察到的) 的结果+通常是右值,因此无需将整个表达式包装在第二个 中std::move。
例如,如果您正在累积std::strings,则std::move(init) + *first可能能够追加*first到 s 缓冲区中保留但尚未使用的空间,init而不必分配一个长度为init和的长度之和的新缓冲区*first。
use*_*522 21
的值类别init + *first并不重要。
initininit + *first是左值。
因此,如果init + *first调用operator+按值获取参数的重载,它将导致该参数的复制构造
init但在 后不再需要的值init + *first,因此将其移至参数中是有意义的。
类似地,operator+通过右值引用获取其第一个参数的重载可用于允许操作修改参数。
这就是std::move这里所取得的成就。
该标准自 C++20 起指定了此行为。
它与 的返回值无关operator +,而是与它的参数有关。
据我所知,一个有效的实现std::accumulate可能是
template<typename ITER, typename T, typename OP = plus>
constexpr inline T accumulate(ITER first, ITER last, T init, OP op = {})
{
for (; first != last; ++first)
init = op(std::move(init), *first); // move the argument that is being overwritten
return init;
}
Run Code Online (Sandbox Code Playgroud)
accumulate用 来指定将是一个更大的重大更改operator +=。为了对称性,您需要更改BinaryOperation重载,这将破坏所有现有用途,就像定义+但未定义的任何类型一样+=。