web-dev-qa-db-ja.com

プリインクリメントとポストインクリメント

Google C++ Style Guide には次のように書かれています:

プリインクリメントとプリデクリメント

プレフィックス形式(++i)イテレータと他のテンプレートオブジェクトを使用した増分演算子と減分演算子。

変数がインクリメントされるとき(++iまたはi++)または減分(--iまたはi--)で、式の値が使用されていない場合は、前インクリメント(デクリメント)か後インクリメント(デクリメント)かを決定する必要があります。

戻り値が無視される場合、「pre」形式(++i)は、「投稿」フォーム(i++)、そしてしばしばより効率的です。これは、ポストインクリメント(またはデクリメント)では、式の値であるiのコピーを作成する必要があるためです。 iがイテレータまたはその他の非スカラー型の場合、iをコピーすると負荷がかかる可能性があります。値が無視された場合、2つのタイプの増分は同じように動作するので、なぜ常に事前増分ではないのですか?

Cでは、式の値が使用されない場合、特にforループでポストインクリメントを使用するという伝統が発達しました。 「サブジェクト」(i)が「動詞」(++)、英語のように。

単純なスカラー(非オブジェクト)値の場合、1つの形式を選択する理由はなく、どちらも許可されます。イテレータおよびその他のテンプレートタイプの場合は、事前増分を使用します。

私は本当に "に同意しないでください。"戻り値が無視されると、 "pre"形式(++i)は、「投稿」フォーム(i++)、そしてしばしばより効率的です。これは、ポストインクリメント(またはデクリメント)では、式の値であるiのコピーを作成する必要があるためです。 "

少なくとも、ポインタに関しては(そしてポインタレジスタは整数として使用できます)。

私のハードウェア時代に戻ると、ポストインクリメント、ポストデクリメント、プレデクリメントが組み込まれたアドレッシングモードの形式のマシン命令がたくさんありました。ポストインクモードとポストデクリモードのコストは、追加の命令0です。まったく増加しないよりもサイクルが多い。次のオペコードがフェッチされている間に命令が実行された後、インクリメントが発生しましたが、事前決定には、レジスタが使用される前に追加の命令サイクルが必要でした。だから、本当にわかりません。誰かがグーグルのケースを私に説明できますか?

あなたはポインタに関して正しいかもしれません。

しかしながら:

  • CはC++ではありません。 C++は、特にRAIIに影響を与えるため、コピーが作成されたときを中心に、いくつかの非常に正確なセマンティクスを保証します。C++では、コピーは観察可能な影響です。

  • すべてのイテレータがポインタであるとは限りませんが、より複雑なオブジェクトになる場合があります。たとえば、std::back_insert_iteratorのようなイテレータや、std::unordered_mapのような不連続なデータ構造に対するイテレータを出力します。

"戻り値が無視される場合、" pre "形式(++ i)は" post "形式(i ++)よりも効率が悪くなることが多く、多くの場合より効率的です。これは、ポストインクリメント(またはデクリメント)では、式の値であるiのコピーを作成する必要があります。 "

少なくともポインタに関しては。

ここで反対することは何もありません。 「++ii++よりも効率が悪いことはありません」は、それらが同等に効率的である可能性があることを意味します(これはポインターについて主張しています)。しかしif違いがあり、++iの方がパフォーマンスが良いと予想されます。

正確に言うと、特定のアーキテクチャでは、ポストデクリメントはより効率的であり、デクリメント前よりもポストデクリメントがより効率的であることを保証します。ただし、このような考慮事項は、特定のマイクロアーキテクチャー向けに開発している場合にのみ適用されます。移植可能なコードの場合、言語自体による保証がより重要です。 AFAIKには、x86もARM=命令セットファミリも、特別なポストインクリメント命令が含まれています。

もちろん、前置インクリメント演算子が意図的に不必要な作業を行う反例を作成することは可能ですが、consistentおよびminimalの定義の後置インクリメントおよび前置インクリメント、ポストインクリメントは、プリインクリメントの観点から実装できます(そしておそらくそうする必要があります)。

struct X {
  ...
  // pre-increment
  X& operator++() { ...; return *this; }

  // post-increment
  X operator++(int) {
    X copy(*this);
    operator++();
    return copy;
  }
};

ポストインクリメントは、インクリメントが実行される前のオブジェクトの状態を返す必要があるため、ユーザー定義型の場合、インクリメント前の状態のコピーは避けられません。

もちろん、十分にスマートなコンパイラーは、順序付けルールとas-ifルールを利用することで、ポストインクリメントをインライン化し、コピーを回避することができます(ささいなコピートラクターを想定)。

14
amon

の代わりに:

「英語のように、「件名」(i)が「動詞」(++)の前にあるため、ポストインクリメントが読みやすくなる人もいます。」

可読性と効率の両方のためにこれを試してください:

英語のように、「オブジェクト」(i)が「動詞」(++)に続くので、プリインクリメントを読みやすくする人もいます。 (これは「理解された主題」の例です。)

「++ i」を「増分i」と読みます。