C++における"&&",|"の重荷問題

2968 ワード

C++では「&&」と「|」はリロードできないと考えられていた.この見方はいくつかの事実に由来する.
まず、セグメントコードを見てみましょう.
#include <iostream>

int func(int i, int j) { return i + j; }

int main()
{
    int a = 1;
    int b = 0;
    b = func(a*=2, a+=1);
    std::cout << b << std::endl;
    return 0;
}

このコードは印刷結果が不確定です.正確なコンパイラでコンパイルして実行すると、正確な結果が得られます.しかし、コンパイラを変えると、結果が異なる可能性があります.どうして?
ここでは、シーケンスポイント(sequence point)の概念について説明します.シーケンスポイントは、式の評価ポイントと考えられます.このポイントが実行されるときに、すべての副作用(side effects)を持つ計算が終了しなければなりません.次のシーケンスポイントの副作用を持つ計算はまだ開始されていません.シーケンスポイントに関する完全な情報は、次のように参照してください.http://en.wikipedia.org/wiki/Sequence_point.
関数呼び出しは、関数体に入る前にシーケンスポイントが存在し、このポイントの前に関数のパラメータがすべて評価されなければならないが、パラメータの評価順序が規定されていないことを規定している.
パラメータの評価順序を限定しないのは,コンパイラメーカーに最適化の空間を残し,コンパイラがパラメータの状況に応じて評価順序を最適化できるようにするためである.コンパイラがどのように最適化されても、プログラマは各パラメータの評価が互いに影響しないことを保証すれば、決定的な結果を得ることができます.
コードfunc(a*=2,a+=1)に戻ります.
  • 上記のように、関数呼び出しのシーケンスポイントは、funcを呼び出す前にa*=2およびa+=1が計算済みであることのみを要求するが、それらの計算順序は規定されていない.
  • デフォルトの_cdecl関数呼び出しは、パラメータのスタック順序は右から左ですが、各パラメータ自体の評価順序には影響しません.
  • したがってa*=2とa+=1のどちらが先に計算されるかは不確定であり,コンパイラが異なる表現を持つことはできない.
  • a*=2が先に計算された場合、結果は6であり、a+=1が先に計算された場合、結果は8である.

  • プログラムの実行結果もこの点を証明した:VS 2010での実行結果は8,VC 6である.0で実行した結果は6です.
    「&&」と「|」を見ると、この2つが評価に合致する場合にも、状況に応じて評価を継続するには、左から評価が完了しなければならないというシーケンスポイントがあります.例えばp!=0&&p->Done()、p->Done()を実行する前にp!=0は既に実行済みである必要があります.また、「&&」は、左の評価値がfalseであれば、式全体がfalseになり、右の表現が評価されないことを保証します.「||」は、左側の評価値がtrueの場合、式全体がtrueになり、右側の式は評価されないことを保証します.すなわち,特定の状況を満たすと,式全体の一部が無視され,電線が短絡したようになるため,短絡評価とも呼ばれ,適切である.大量のコードの正確性は,&&,"|"のシーケンスポイント規定と短絡評価特徴に依存する.
    もし私たちが「&&」をリロードしたとしたら、p!=0&&p->Done()の場合、実際にはoperator&(p!=0、p->Done()が実行されます.つまり、リロード後に「&&」は関数呼び出しに退化し、関数呼び出しのシーケンスポイントルールに従います.つまりこの時、p!=0とp->Done()は、評価順序が不確定になるだけでなく、常に評価されます.では、pが本当に0になると、p->Done()も実行され、一般的には良い結果は得られません.
    これは私が何年も前から考えていたことで、「&&&」「|」はリロードできないと断言していたが、昨日までC++の中でリロードできない記号のリストを探していたところ、中には「&&&」、「|」が入っていなかった.同時に自分でプログラムを書いてみて、リロードできることも確認しました.私もなぜこんなに長い間この問題を発見したのかを反省しなければならない.
    添付:C++でリロードできない関数のリスト
       Operator Name                           Syntax Bind pointer to member by reference    a.*b Member                                      a.b Scope resolution                             a::b Size of                                              sizeof(a) Ternary                                             a ? b : c Type identification                              typeid(a)
    『The Design and Evolution of C++』には「.」重荷されてはいけない理由は、とてもよく話していて、興味のあるものは見てもいいです.