[英]Use of std::move in std::accumulate
In my Fedora 34 environment (g++), std::accumulate
is defined as:在我的 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;
}
If the expression init + *first
is already an rvalue, what is the purpose of std::move
?如果表达式
init + *first
已经是一个右值,那么std::move
的目的是什么?
The value category of init + *first
doesn't matter. init + *first
的值类别无关紧要。
init
in init + *first
is a lvalue. init
init + *first
中的 init 是一个左值。
So if init + *first
calls an operator+
overload taking the parameter by-value, it will cause a copy construction of that parameter因此,如果
init + *first
调用按值传递参数的operator+
重载,它将导致该参数的复制构造
But the value of init
is not required anymore after init + *first
, so it makes sense to move it into the parameter instead.但是在
init + *first
之后不再需要init
的值,因此将它移到参数中是有意义的。
Similarly a operator+
overload taking its first argument by rvalue-reference might be used to allow modification of the argument by the operation.类似地,通过右值引用获取其第一个参数的
operator+
重载可用于允许操作修改参数。
This is what std::move
achieves here.这就是
std::move
在这里实现的。
The standard specifies this behavior since C++20.该标准从 C++20 开始指定此行为。
std::move(init) + *first
can sometimes generate more efficient code than init + *first
, because it allows init
to be overwritten. std::move(init) + *first
有时可以生成比init + *first
更高效的代码,因为它允许覆盖init
。 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
.但是,由于(如您所见)
+
的结果通常是右值,因此无需将整个表达式包装在第二个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
.例如,如果您正在累积
std::string
s,那么std::move(init) + *first
可能能够 append *first
进入init
缓冲区中保留但尚未使用的空间,而不是拥有分配一个新的缓冲区,其长度是init
和*first
的长度之和。
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.您没有显示宏扩展为什么,但是当我昨天写了一个 bignum 加法示例作为课程时,我只是被提醒了,为什么这样做更有效。
The binary +
operator creates and returns a temporary object.二进制
+
运算符创建并返回一个临时 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.这种编码风格让我觉得有点奇怪——正如我所提到的,
+=
正是程序员在这里想要的语义,所以我不确定他们为什么不使用它。
It's nothing to do with the return value of operator +
, but with the arguments to it.这与
operator +
的返回值无关,而是与 arguments 相关。
As far as I can tell, a valid implementation of std::accumulate
could be据我所知,
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;
}
It would be a larger breaking change to specify accumulate
in terms of operator +=
.根据
operator +=
指定accumulate
将是一个更大的突破性变化。 For symmetry you'd want to change the BinaryOperation overloads, and that would break all the existing uses, as would any type that defined +
but not +=
.为了对称,您需要更改BinaryOperation重载,这将破坏所有现有用途,任何定义
+
但未定义+=
的类型也是如此。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.