过早的优化还是我疯了?

发布于 2024-09-14 01:28:00 字数 447 浏览 7 评论 0原文

我最近在 comp.lang.c++ 上看到一段代码,它从函数返回静态整数的引用。代码是这样的

int& f()
{
   static int x;
   x++;
   return x;
}

int main()
{
  f()+=1; //A
  f()=f()+1; //B
  std::cout<<f();

}

当我使用我很酷的 Visual Studio 调试器调试应用程序时,我只看到了对语句 A 的一次调用,猜猜我震惊了什么。我一直认为 i+=1 等于 i=i+1 所以 f()+=1 将等于 f()=f()+1 并且我会看到对 f() 的两次调用>,但我只看到了一个。这到底是什么?我疯了还是我的调试器疯了或者这是过早优化的结果?

I recently saw a piece of code at comp.lang.c++ moderated returning a reference of a static integer from a function. The code was something like this

int& f()
{
   static int x;
   x++;
   return x;
}

int main()
{
  f()+=1; //A
  f()=f()+1; //B
  std::cout<<f();

}

When I debugged the application using my cool Visual Studio debugger I saw just one call to statement A and guess what I was shocked. I always thought i+=1 was equal to i=i+1 so
f()+=1 would be equal to f()=f()+1 and I would be seeing two calls to f(), but I saw only one. What the heck is this? Am I crazy or is my debugger gone crazy or is this a result of premature optimization?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

勿挽旧人 2024-09-21 01:28:00

《标准》对 += 和朋友是这样说的:

5.17-7:E1 op= E2 形式的表达式的行为相当于
E1 = E1 op E2 除了 E1 是
仅评估一次。[...]

所以编译器在这一点上是正确的。

This is what The Standard says about += and friends:

5.17-7: The behavior of an expression of the form E1 op= E2 is equivalent to
E1 = E1 op E2 except that E1 is
evaluated only once.[...]

So the compiler is right on that.

我不吻晚风 2024-09-21 01:28:00

i+=1 功能i=i+1 相同。它实际上以不同的方式实现(基本上,它的设计是为了利用 CPU 级别的优化)。

但本质上左侧仅被评估一次。它产生一个非常量左值,这就是读取该值、加一并将其写回所需的全部内容。

当您为自定义类型创建重载运算符时,这一点更加明显。 operator+= 修改 this 实例。 operator+ 返回一个新实例。通常建议(在C++中)先写oop+=,然后根据它写op+。

(请注意,这仅适用于 C++;在 C# 中,op+= 与您的假设完全一样:只是 op+ 的简写形式,您无法创建自己的 op+=。它是从 Op+ 中自动为您创建的)

i+=1 is functionally the same as i=i+1. It's actually implemented differently (basically, it's designed to take advantage of CPU level optimization).

But essencially the left side is evaluated only once. It yields a non-const l-value, which is all it needs to read the value, add one and write it back.

This is more obvious when you create an overloaded operator for a custom type. operator+= modifies the this instance. operator+ returns a new instance. It is generally recommended (in C++) to write the oop+= first, and then write op+ in terms of it.

(Note this is applies only to C++; in C#, op+= is exactly as you assumed: just a short hand for op+, and you cannot create your own op+=. It is automatically created for you out of the Op+)

溺孤伤于心 2024-09-21 01:28:00

你的想法是合乎逻辑的,但不正确。

i += 1;
// This is logically equivalent to:
i = i + 1;

但逻辑上等价和相同并不相同。
代码应该看起来像这样:

int& x = f();
x += x;
// Now you can use logical equivalence.
int& x= f();
x = x + 1;

编译器不会进行两个函数调用,除非您显式地将两个函数调用放入代码中。如果你的函数有副作用(就像你所做的那样),并且编译器开始添加额外的难以看到的隐式调用,那么实际上很难理解代码的流程,从而使维护变得非常困难。

Your thinking is logical but not correct.

i += 1;
// This is logically equivalent to:
i = i + 1;

But logically equivalent and identical are not the same.
The code should be looked at as looking like this:

int& x = f();
x += x;
// Now you can use logical equivalence.
int& x= f();
x = x + 1;

The compiler will not make two function calls unless you explicitly put two function calls into the code. If you have side effects in your functions (like you do) and the compiler started adding extra hard to see implicit calls it would be very hard to actually understand the flow of the code and thus make maintenance very hard.

冰魂雪魄 2024-09-21 01:28:00

f() 返回对静态整数的引用。然后 += 1 向该内存位置加一 - 无需在语句 A 中调用它两次。

f() returns a reference to the static integer. Then += 1 adds one to this memory location – there's no need to call it twice in statement A.

粉红×色少女 2024-09-21 01:28:00

在我见过的每一种支持 += 运算符的语言中,编译器都会对左侧的操作数求值一次,以生成某种类型的地址,然后使用该地址读取旧值并写入新值。 += 运算符不仅仅是语法糖;它还是语法糖。正如您所注意到的,它可以实现表达语义,而通过其他方式很难实现这一点。

顺便说一下,vb.net 和 Pascal 中的“With”语句都有类似的功能。像这样的声明:

' Assime Foo is an array of some type of structure, Bar is a function, and Boz is a variable.
  With Foo(Bar(Boz))
    .Fnord = 9
    .Quack = 10
  End With

will compute the address of Foo(Bar(Boz)), and then set two fields of that structure to the values nine and ten. It would be equivalent in C to

  {
    FOOTYPE *tmp = Foo(Bar(Boz));
    tmp->Fnord = 9;
    tmp->Quack = 10;
  }

但 vb.net 和 Pascal 不公开临时指针。虽然在 VB.net 中无需使用“With”来保存 Bar() 的结果就可以实现相同的效果,但使用“With”可以避免使用临时变量。

In every language I've seen which supports a += operator, the compiler evaluates the operand of the left-hand side once to yield some type of a address which is then used both to read the old value and write the new one. The += operator is not just syntactic sugar; as you note, it can achieve expression semantics which would be awkward to achieve via other means.

Incidentally, the "With" statements in vb.net and Pascal both have a similar feature. A statement like:

' Assime Foo is an array of some type of structure, Bar is a function, and Boz is a variable.
  With Foo(Bar(Boz))
    .Fnord = 9
    .Quack = 10
  End With

will compute the address of Foo(Bar(Boz)), and then set two fields of that structure to the values nine and ten. It would be equivalent in C to

  {
    FOOTYPE *tmp = Foo(Bar(Boz));
    tmp->Fnord = 9;
    tmp->Quack = 10;
  }

but vb.net and Pascal do not expose the temporary pointer. While one could achieve the same effect in VB.net without using "With" to hold the result of Bar(), using "With" allows one to avoid the temporary variable.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文