7

私は最近、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 への呼び出しが 1 回だけ表示され、何がショックだったのかを推測しました。私は常にi+=1が等しいと思っていたi=i+1ので f()+=1が等しい とf()=f()+1が 2 回呼び出されるとf()思っていましたが、1 つしか表示されませんでした。何じゃこりゃ?私は気が狂っていますか、それとも私のデバッガーは狂っていますか、それとも時期尚早の最適化の結果ですか?

4

5 に答える 5

27

これは、標準が+=友人について述べていることです:

5.17-7: E1 op= E2 の形式の式の動作は、E1 が 1 回だけ評価されることを除いて、E1 = E1 op E2 と同等です。

したがって、コンパイラはその点で正しいです。

于 2010-08-09T14:13:15.713 に答える
10

i+=1機能的に同じi=i+1です。実際には実装方法が異なります (基本的に、CPU レベルの最適化を利用するように設計されています)。

ただし、本質的に左側は一度だけ評価されます。値を読み取り、1 を追加し、書き戻すために必要なのは、これだけです。

これは、カスタム型のオーバーロードされた演算子を作成する場合により明白になります。 operator+=インスタンスを変更しthisます。 operator+新しいインスタンスを返します。一般に、(C++ では) 最初に oop+= を記述し、次にそれに関して op+ を記述することをお勧めします。

(これは C++ にのみ適用されることに注意してください。C# では、op+=あなたが想定したとおりです: の省略形でop+あり、独自の op+= を作成することはできません。Op+ から自動的に作成されます)

于 2010-08-09T14:06:03.157 に答える
9

あなたの考えは論理的ですが、正しくありません。

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;

コードに 2 つの関数呼び出しを明示的に配置しない限り、コンパイラは 2 つの関数呼び出しを行いません。関数に副作用があり(あなたのように)、コンパイラーが暗黙の呼び出しを確認するのが非常に困難になった場合、コードの流れを実際に理解するのが非常に難しくなり、メンテナンスが非常に困難になります。

于 2010-08-09T14:08:34.757 に答える
3

f()静的整数への参照を返します。次に+= 1、このメモリ ロケーションに 1 を追加します。ステートメント A で 2 回呼び出す必要はありません。

于 2010-08-09T14:08:40.793 に答える
0

+= 演算子をサポートする私が見たすべての言語で、コンパイラは左側のオペランドを一度評価して、古い値の読み取りと新しい値の書き込みの両方に使用されるある種のアドレスを生成します。+= 演算子は単なる構文糖衣ではありません。お気づきのように、他の手段では実現しにくい式のセマンティクスを実現できます。

ちなみに、vb.net と Pascal の "With" ステートメントには、どちらも同様の機能があります。次のようなステートメント:

' Assime Foo はある種の構造体の配列、Bar は関数、Boz は変数です。
  Foo(バー(ボズ))で
    .Fnord = 9
    .Quack = 10
  で終わる
Foo(Bar(Boz)) のアドレスを計算し、その構造体の 2 つのフィールドに値 9 と 10 を設定します。Cでは同等です

  {
    FOOTYPE *tmp = Foo(バー(ボズ));
    tmp->Fnord = 9;
    tmp->Quack = 10;
  }

ただし、vb.net と Pascal は一時ポインターを公開しません。"With" を使用して Bar() の結果を保持しなくても VB.net で同じ効果を実現できますが、"With" を使用すると一時変数を回避できます。

于 2010-08-09T15:29:38.727 に答える