インクリメント演算子とデクリメント演算子(++-)には、コードをより単純にするための単純な使用よりも多くの目的があるのではないかと思いました。
多分:
i++;
より効率的です:
i = i + 1;
?
多くの点で、演算子の主な目的は下位互換性です。C++ が設計されていたとき、一般的なルールは、少なくとも非クラス型に対して C が行ったことを行うことでした。C に and がなかった++
と--
したら、C++ にそれらがあるとは思えません。
もちろん、これは疑問を投げかけます。それらが最新のコンパイラで異なるコードを生成することは考えられませんし、委員会が最適化の理由でそれらを導入することもまったく考えられません (ただし、移動セマンティクスが主に最適化の理由で導入されたことはわかりません)。しかし、1970 年代半ば、C の形成期でしたか? 当時は、PDP-11 の機械語命令に対応するため導入されたと一般に信じられていました。一方、B には既に存在していました。C は B から取得しました。また、B はインタプリタ言語であるため、機械命令に対応するという問題はありませんでした。多くの演算子 (&
ではなく、and
など) は、当時の開発が主にテレタイプ (tty) で行われ、テレタイプに出力するすべての文字が多くの不快なノイズを発生させたため、それらが導入されたということです。したがって、必要な文字数が少ないほど良いです。
とのどちらを選択するかについては++ i;
、を繰り返さなくてもよいという明確な利点があるため
(もちろん、多かれ少なかれ複雑な式になる可能性があります)、少なくとも. Python は、代入を任意の式の副作用としてではなく、ステートメントとして扱う以外の理由がない場合、そこで停止します。C と C++ で 30 年以上のプログラミングを行ってきた私は、代入をステートメントとして扱うように C++ をかなり制限しているにもかかわらず (そして、より複雑な式に埋め込まないようにしています)、Python でプログラミングするときにそれが欠けていると感じています。 )。i += 1;
i = i + 1;
i
i += 1;
++ i
++ i
あなたが示した 2 つの例は、ほぼ確実にまったく同じマシン コードにコンパイルされます。コンパイラは非常に賢いです。実際に書いたコードをコンパイラが実行することはめったにないことを理解してください。それをねじって成形し、性能を向上させます。最新のコンパイラは、i++;
とi = i + 1;
が (算術型に関して) 同一であることを認識し、同じ出力を生成します。
ただし、コードの読みやすさだけでなく、両方を備えているのには十分な理由があります。概念的には、値を何度もインクリメントすることと値を追加することは異なる操作です。ここでは 1 を追加しているため、同じです。 などの追加x + 3
は 1 つの操作ですが、実行++++++x
は同じ効果を持つ 3 つの個別の操作を表します。もちろん、スマート コンパイラは、算術型 のオブジェクトの場合、を実行するだけで定数時間でインクリメントx
できることも認識しています。N
x + N
ただし、オーバーロードされたandx
を持つクラス型の場合、コンパイラはこの仮定を行うことができません。これら 2 つのオペレーターは、まったく異なることを行う場合があります。さらに、を非一定時間操作として実装すると、誤った印象を与える可能性があります。operator+
operator++
operator+
イテレータを扱っていると、両方を持つことの重要性が明らかになります。加算と減算をサポートするのはランダム アクセス反復子のみです。たとえば、標準の raw ポインターはptr + 5
、5 番目のオブジェクトへのポインターを実行して取得できるため、ランダム アクセス反復子です。ただし、他のすべてのタイプのイテレータ (双方向、前方、入力) はこれをサポートしていません。インクリメントのみが可能です (双方向イテレータはデクリメントできます)。双方向反復子と共に 5 番目の要素に到達するには、++
5 回実行する必要があります。これは、加算が一定時間の操作を表すためですが、多くのイテレータは単純に一定時間内にトラバースできないためです。複数のインクリメントを強制することは、一定時間の操作ではないことを示しています。
の種類によって性能が異なりi
ます。
組み込み型の場合、オプティマイザーは 2 つのステートメントが同じであることを「認識」し、両方に対して同じコードを発行します。
後置インクリメントを使用した (そしてセミコロンを無視した) ため、i
が組み込み型の場合でも、2 つの式の値は異なります。の値i++
は古い値で、の値i = i + 1
は新しい値です。したがって、次のように書くと:
j = i++;
k = (i = i + 1);
この 2 つは異なるものになり、両方に対して同じコードが出力されることはありません。
事後インクリメントの事後条件は事前インクリメントと同じであるため、事後インクリメント演算子の主な目的は、異なる値に評価されることであると言えます。パフォーマンスに関係なく、言語の表現力が向上します。
i
オーバーロードされた演算子を持つクラス型がある場合、それほど単純ではない可能性があります。理論的には、この 2 つの結果は同じではないかもしれませんが、「適切な」演算子i + 1
がクラスの新しいインスタンスを返すと仮定すると、そのインスタンスは移動代入されます (型に移動代入演算子がある場合は C++11 で) または copy-に (C++03 で) 割り当てられi
ます。operator++
特に演算子のオーバーロード定義が別の翻訳単位でオフになっている場合、オプティマイザーがこれを実際と同じくらい効率的にできるという保証はありません。
一部のタイプには、たとえばがありますがoperator++
、ありません。ただし、これは C に存在し、C には with but notの型がないため、存在する理由ではありません。これは、C++ が既存のものを利用した方法です。operator+
std::list<T>::iterator
++
++
+
いいえ、それは単にタイピングを単純にし、構文をより単純にすることです。
両方がコンパイルされると、同じコンパイル済みコードに縮小され、同じ速度で実行されます。
i++
との違いi = i + 1
は、最初の式は 1i
回だけ評価され、2 番目の式は 2 回評価されることです。検討:
int& f() {
static int value = 0;
std::cout << "f() called\n";
return value;
}
f()++; // writes the message once
f() = f() + 1; // writes the message twice
また、f()
異なる呼び出しで異なる参照を返す場合、2 つの式の効果は大きく異なります。もちろん、これが良いデザインであるというわけではありません...
++
or演算子を使用すると、--
ではなく他のステートメントに結合できますi=i+1
。たとえばwhile (i++ < 10)
、while ループ チェックとその後のインクリメントを許可します。でそれを行うことはできませんi=i+1
。
++
or演算子を他の--
クラスでオーバーロードして、他の意味を持たせたり、追加のアクションでインクリメントおよびデクリメントを行うことができます。詳しくはこちらのページをご覧ください。
どちらも、まともなコンパイラによって最適化されたコードで同じマシン命令を生成します。
コンパイラi++
がより効率的であると判断した場合は、 に変換i=i+1
されi++
ます。逆も同様です。何を書いても結果は同じです。
私は好き++i
です。私は決して書きませんi=i+1
。
どちらもまったく同じではありませんが、機能的には同じですが、優先順位に違いがありi++
、i=i+1
++i または i++ をより複雑な式に置き換えてみてください。プリインクリメント/ポストインクリメント機能には問題があります。式を複数に分割する必要があります。三項演算子のようなものです。これは同等です。コードの入力方法に関係なく、コンパイラはいくつかの最適化を実行する場合がありますが、三項演算子とプリインクリメント/ポストインクリメント構文は、コードのスペースと読みやすさを節約するだけです。