teppy世の中には様々な理由で生配列を直接使う場合があります。0;}要素数が変わったらarray_sizeの数値も忘れずに変更しなければいけませんが、必ず忘れることでしょう。配列の要素は静的に決定するので計算で求めることができます。C言語の時代から人類は500万回以上も配列の要素数取得マクロを再発明してきました。大体、どんなプロジェクトを覗いても配列サイズ取得のマクロを用意してあることが多かったです。実際、どれほど使われていたかは定かではありませんが......。(sizeof(a)/sizeof(a[0]))全体のサイズを1要素のサイズで割ることで配列の要素数を計算できます。これを使えば要素数が増えた時でも自動的に値が更新されます。0;}ただマクロでは名前空間の指定ができません。C++的にはテンプレートで書くのがいいでしょう。N;}関数なので任意の名前空間に閉じ込められます。応用すれば2次元配列の要素も簡単に一発で取得できます。これらの取得方法は使えません。結局、生配列を取り回し良く使うには、先頭アドレスと要素数とのペアとして管理する必要が出てきます。C++の恩恵を最大限に活用する為にもコンテナクラスを使いましょう。teppyC++を書きはじめて半年もすると、ほとんどのプログラマはクラスの定義を見ただけで生成されるオブジェクトのメモリレイアウトが浮かんでくるようになります。0005このように一目です。(ただし浮かんでくるだけで合っているとは言っていない)基本的にクラス内のメンバ変数は定義された順にメモリ上に並びます。単一継承の場合、親クラス→子クラスというメモリレイアウトになります。複雑な設計になるとどうでしょうか。d;};多少複雑になりました。構造体DDDは仮想関数を持ち、多重継承しています。0013これは64bit環境における一例です。元のコードだけでは圧倒的に情報が不足しているため、この構造体定義から導き出されるメモリレイアウトは無数にあります。ある意味、どんなレイアウトを想像しても正解と言えなくもありません。intは4byteかもしれませんし、8byteかもしれません。vtableのポインタは多くの場合メンバの最初に置かれますがこれはコンパイラ依存です。そもそも動的なディスパッチを仮想関数テーブルによって実装しない場合はvtableはありません。同様に多重継承した場合のレイアウトも宣言順とは限らず不定です。クラスのメモリレイアウトに関して少しマイナーな情報もあります。シンプルな例、構造体AAAに戻って話を進めましょう。メモリレイアウトは不定です。宣言順にabcdeと順序よく配置される保証はありません。C++の規格ではあるアクセス指定子の範囲において宣言された順にレイアウトされることは保証していますがアクセスレベルを超えた宣言についてのメモリレイアウトには規程がありません。(11).__n3337この場合d,eが連続したメモリに配置されることのみ保証されます。aもbもprivateメンバですが、アクセス指定子を超えた場所で宣言されているのでレイアウトの保証対象外です。あなたが複数のプラットフォーム向けにビルドする予定のアプリケーションを書く可能性があるのなら、どのように書くとメモリレイアウトが「不定」になるかを知ることは非常に重要です。teppyこのアーティクルでは前置と後置、2種類のインクリメントについて考察と現状をまとめるものです。従来(~2015)は前置インクリメント(++value)と後置インクリメント(value++)の挙動の違いを正しく完全に理解した上で前置インクリメントを使うべきだ。いうのがC++プログラマの基本姿勢でした。各インクリメントの標準的な実装を以下に示します。コピーしたものを返す}後置インクリメントにはひと目で遅くなりそうな処理が見て取れますね。前置インクリメントがインクリメント処理後、単純に自身の参照を返すのに対し、後置インクリメントではインクリメント前に一時オブジェクトの生成、そしてインクリメント後にはその前に生成した一時オブジェクトを値で返しています。前置と後置では、単純にオブジェクトをコピーして返す分、普通に考えたら後置の方が遅いよね。いうのが従来の認識でした。-101のルール、ガイドライン、ベストプラクティス」の中でも、特に後置インクリメントの必然性が無い時は迷わず前置インクリメントを使うことが推奨されてきました。「ゲームエンジン・アーキテクチャ第二版」の中の一節を紹介します。値が使われる場合、CPUのパイプラインでストールを生じさせないので、ポストインクリメントの方が優秀である。したがって、プレインクリメントの動作が絶対に必要である場合を除いて、必ずポストインクリメントを使う習慣を身につけたほうがよい。ポストインクリメント従来の主張と真逆の主張が展開されたのです。後置インクリメントの方が優秀なので出来る限りそちらを使うべきとされました。Usの開発元であるノーティドッグ社のエンジニアです。その彼が後置インクリメントを使うべきと主張したことで大きな話題となりました。後置インクリメントが優秀とされる理由は以下のとおりです。前置インクリメントは値を書き換えて戻すのでインクリメント処理が終了するまで戻す値が決まらないそれはデータ依存性を生み、深いパイプラインのCPUではストールを発生させる後置インクリメントはインクリメント処理と戻す値は別インスタンスなので並列に処理が可能よってストールの発生が無い分、後置インクリメントが優秀なるほど、といった感じです。考察を深めよう今後は後置インクリメントに統一だ!今まで書いた前置インクリメントを全部置換しなくちゃ!と、急ぐ前に、本当にゲームエンジン・アーキテクチャの主張が正しいかは一考の余地があります。深いパイプラインにおけるストールの可能性はデータ依存性があるかぎり発生しうる問題です。この部分の主張は正しいです。ゲームエンジン・アーキテクチャでは後置インクリメントに発生していたコピーコストについて言及がありません。文脈を読み解く限り、「コピーコスト<ストールコスト」という前提があるようです。このインクリメントの項以外でもゲームエンジン・アーキテクチャではストールに対する嫌悪を書いてある箇所があり、著者はかなりストールコストに対してナイーブになっているように感じます。実際にコンソールゲーム機のアーキテクチャにはストールのコストが引くほど高いものもあり、それこそPS2の時代はとにかくCPUの稼働効率を高めるようなコードを意識して書かないと描画ポリゴンが倍は違うというような状況でした。XBOX360の頃でさえ、シーングラフ(ゲームシーンの描画を効率的に管理したりする構造)を作っても、その走査や判定のコストの方が高い為、単純にシーンにある全オブジェクトを順番に描画するだけの方が速かったという経験があります。一時オブジェクトの生成に伴うコストについて、好意的に解釈してみましょう。例で示した実装の場合、後置インクリメントで変更前の値を返す際にRVOの最適化が期待されるので、前置インクリメントも後置インクリメントも(このインクリメントの結果として使う時の)オブジェクトの生成は1度きりになる可能性があります。Optimization)に対応している必要があります。それでもほとんどのコンパイラはNRVOが効くのでコピーコストは低くなる可能性があります。NRVOが効くコンパイラ(今回はgcc)を使って実際のアセンブラコードを見てみましょう。ユーザー定義の前置インクリメントと後置インクリメントを用意します。b_;};前置インクリメント、後置インクリメントそれぞれの処理を呼び出す処理を書きます。0;}これら2つの処理のアセンブラコードを出力します。-O0アセンブラコードを読むのが嫌いな人は行数だけ見ればいいです。4LEFDE1:.def_printf;.scl2;.type32;.endefやっぱり後置インクリメントの方が遅いですね。次はコンパイラの最適化オプションをバッチリ有効にしたバージョンで見てみましょう。4LEFDE1:.def_printf;.scl2;.type32;.endefなんということでしょう。前置も後置もまったく同じ結果になりました。昨今のコンパイラは優秀です。これは例として示したコードのようにインクリメントの実装詳細まで一つのコンパイル単位に含まれている関係で、コンパイラが副作用がないことを把握しながらインライン展開まで行える為、とにかく結果だけ合うように最速のパフォーマンスを組み上げられる為です。一歩進んで、Hoge::operator++()とHoge::operator++(int)を別のcppに移しました。old;}それではアセンブラコードを見てみましょう。-O3まずはhoge.cpp自体のアセンブラコード。4LEFDE1:.def__ZN4HogeppEi;.scl2;.type32;.endef.def_printf;.scl2;.type32;.endefやはりコード次第では、最適化オプションを有効にしていても、前置インクリメントにアドバンテージがあるケースがありますね。hoge.cppなんてコンパイラにとったら何のヒントも無いコンパイル単位なので、素直に処理するしかないので当たり前といえば当たり前です。世の中には、リンク時最適化というものがあり、オブジェクトコード生成後に再度ファイルを走査し、リンクのタイミングでインライン展開されるような場合もあるので、この例で一概に前置が有利と結論付けることはできません。ストールコストの考察ストールが発生するシチュエーションを考えます。ストールは本来、このインクリメントに限らず、データ依存性を持つあらゆるシチュエーションで発生する恐れがあります。条件分岐、値の更新など、結果を待たなければ先の処理が行えないケースが発生するたびストールは起こりえます。分岐予測など、ストールが実質的に無くなるような工夫ももちろん行われているのですが、予測が外れた場合には、事前準備の甲斐なく、やっぱりストールします。インクリメントの外、インクリメント結果を使う瞬間に発生するストールについてですが、ただ単に一行C++++value;1++value;と書く場合、前置と後置で何も変わりません。次の行以降で使う値はインクリメント後の値なので、前置後置関係無く、値の更新が完了しないと何も始まらないからです。exprに入った段階でiの内容が確定している為、インクリメントの結果を待たずに次の処理を進めることができます。いうようなシチュエーションでは前置と後置でストールによるコストが変わってきます。インクリメントの中で発生するストールもについても意識する必要がります。前述のとおり、ストールはどこでも発生する可能性があります。このストールがインクリメント演算の中で発生した場合、インクリメント演算の度にCPUはストールしてしまいます。後置インクリメントであれば、インクリメント演算の結果を待たず次の処理へ移れる為、有利と言えるでしょう。最悪の場合、インクリメントの中でストールするわ、インクリメントの外でストールするわと、春先の有楽町と同じくらいストールで溢れかえってしまいます。Fashion)」と呼びます。今回のゲームエンジン・アーキテクチャの主張もまさに、後置インクリメントはインクリメントの結果を待たなくても、戻り値のオブジェクトは決定しているのでそのオブジェクトに対する処理はインクリメントの動作と並行して行えるよね。