在C++中,A+=B与A=A+B相比是否更可取,就像++A比A++更可取一样?

7
在C++中,A+=B是否比A=A+B更可取,就像++A比A++一样?
我理解"++A"前缀递增至少与"A++"后缀递增一样快。这个问题在很多地方讨论过,包括这里这里。同样地,预计A+=B至少与A=A+B一样快,如此处所示
我正在研究"++":
//From https://herbsutter.com/2013/05/13/gotw-2-solution-temporary-objects/
T T::operator++(int)() {
auto old = *this; // remember our original value
++*this;          // always implement postincr in terms of preincr
return old;       // return our original value
}

我的推断是,在最坏的情况下(可能来自复杂的对象类型) A=A+B 必须检索并存储 A 和添加 B,然后将其保存回原始的 A 位置,而 A+=B 只需将 B 添加到 A 中即可。我的推论正确吗?
预计基本类型在编译时重排为相同,并且这实际上仅适用于需要进行运算符重载的复杂对象。
这是否普遍适用于大多数命令式语言?

在某些架构上,最好使用a++。 - user207421
2
如果您对这种优化水平感到担忧,也许您不应该使用高级语言... - Scott Hunter
1
回复:“这是否普遍适用于大多数命令式语言?”:C++甚至具有非原始类型的语言级值赋值概念,这在大多数其他语言的上下文中甚至都没有意义。 (当然,程序员可以定义setDataplusadd,使得a.add(b)等同于a.setData(a.plus(b)),但此时您应该真正查看A类的文档,而不是试图从一般原则出发进行推理。) - ruakh
2
"... "++A"的前增量操作速度保证至少和"A++"的后增量操作一样快." 这显然是错误的。并不存在这样的保证。在大多数架构和编译器实现中,前增量不太可能会更慢,尤其是对于重载这些运算符的对象,但这当然是有可能的。对于基本类型来说,除非你的编译器很糟糕,否则它们的速度可能相同。 - BeeOnRope
3
人们何时才会意识到 C++ 是一种抽象语言。对于此类底层操作进行的针对“性能”的过分追求在定义上是毫无意义的。 - Lightness Races in Orbit
投票关闭,因为主要基于个人观点。 - Carey Gregory
5个回答

6

我认为这两种方式的原因并不完全相同。我更喜欢使用A += B而不是A = A + B的主要原因是简洁明了。 A += B 明确表示要将 B 添加到 A 中。如果 A 是一个更复杂的表达式,这一点尤其重要:

node_capacities[std::make_pair(node1,node2)] += edge.capacity;

我从未发现 A += B 的性能比 A = A + B 差。


4
重要的事情是要记住,C++拥有运算符重载,这意味着+=运算符可能与您的期望完全不同。
当"目标"类没有定义+=运算符时,+=运算符仅作为加法和赋值工作。运算符重载还意味着例如x++,取决于x的类实例为运算符重载定义的不同含义。

1
“+=”运算符只有在“目标”类未定义“+=”运算符时才能作为加法和赋值运算符使用。 -- 啥?如果“目标”类没有定义“operator+=”,那么“+=”根本不能对它进行操作。 - Benjamin Lindley
2
这是正确的(或者一旦您解决了Benjamin Lindley的评论后就会变成正确的),但我认为它并没有真正回答问题。了解到一些C++代码很糟糕是好的,这样您就知道在决定是否允许其进入项目之前要查看代码;但显然OP更感兴趣的是如何使用已经允许进入项目的非糟糕代码的最佳方法。(顺便说一句,OP显然已经知道运算符重载,因为他/她提到了它,并且给出了用++x实现x++的示例。) - ruakh
1
不确定这是否有太大的意义。在C++中,每一行代码都可能与您期望的完全不同。没有人应该在没有五位不同神明的书面许可下为“有趣”的目的重载运算符。 - Roddy

3

你应该选择A += B;

如果您以前编写的类型中,A = A + B;A += B;更好用,那么您应该更改operator+=的实现方式,使其与A = A + B;完全相同。

然而,反过来通常是不可能的;通常无法以任何合理的方式修改operator+,使A = A + B;完全做到与A += B;相同。

因此,如果A += B;A = A + B;之间有区别,您应该选择A += B;


啊,好的。这是个好点子 - 因为A=A+B将是一个三元自定义运算符。我正在阅读《C++11草案规范n3242.pdf》和关于它的这个问题,但似乎并没有这些内容。 - MathFromScratch

1
在大多数情况下,A+=B更可取。正如您指出的那样,A = A + B与A+=B相比需要更多的寄存器加载。还值得注意的是阅读SSA。这用于编译器优化,可能有助于了解如何处理这种情况。正如您所说,由于编译器的原因,大多数这些考虑因素都不需要程序员自己处理,但了解这些事情是很好的。
在复杂对象中需要考虑的另一个问题是调用getter可能引起的副作用,以及调用此类getter两次可能带来的后果。(想想,A.get() = A.get() + B.get())。然而,在大多数情况下,getter不应该具有副作用,因此这种情况应该相对较少发生。

在你的代码"A.get() = A.get() + B.get()"中,A的get()方法会返回一个地址吗?如果不是,为什么不应该改为"A.set(A.get()+B.get())"呢? - MathFromScratch

-3

实际上,在C++中,您应该避免使用后缀运算符:

  • ++a //这里运算符++是前缀的
  • a++ //这里运算符++是后缀的

仅仅因为后缀解决方案将使用a的值然后增加其值,这可能有时很危险,或者至少会降低一些性能。

您可以在这里查看更多信息。


2
这并没有回答问题。 - anatolyg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接