2011-01-07 11 views
13

C++では、ほとんどの最適化は、if-ifルールから派生しています。つまり、最適化が行われなかった場合にプログラムが動作する限り、それらは有効です。空のデータメンバーの最適化:可能でしょうか?

空の基本最適化はそのようなトリックです:条件によっては、基本クラスが空の場合(非静的なデータメンバーを持たない場合)、コンパイラはそのメモリ表現を削除することがあります。 n3225から、[クラス]

は、どうやらそれはまだ場所の少なくとも1バイト分を取る必要があり、それはデータメンバが空でもあり、標準のデータメンバーに、この最適化を禁止しているようです

4 - クラス型の完全なオブジェクトとメンバサブオブジェクトは、非ゼロのサイズを持つものとする。

注:これは私がよう-かのルールを使用して、場合、1はまだ可能性が思っていたときに、適切な

EBOがでキック持っているために、政策設計のための民間の継承を使用することにつながりますこの最適化を実行することができます。


編集:回答やコメントの数を、次の、そして私が疑問に思って何のことをより明確にします。

はまず、私は例をあげてみましょう:

struct Empty {}; 

struct Foo { Empty e; int i; }; 

私の質問は、なぜsizeof(Foo) != sizeof(int)ですか?特に、パッキングを指定しない限り、Fooはintの2倍のサイズになる可能性があります。これは不思議なほどに膨らんでしまいます。

注:sizeof(Foo) != 0ですなぜ私の質問は何のサブオブジェクトがゼロの大きさを持っていない可能性があるため、それが、これは実際にC++によるとEBOのいずれか

によって必要とされていない、ではありません。

struct Bar: Empty { int i; }; 

がそうである(EBOのおかげ)sizeof(Bar) == sizeof(int)に従う:ただし塩基は、したがって、ゼロサイズ(EBO)を有することを許可されています。

スティーブ・ジェサップは、2つのサブオブジェクトが同じアドレスを持たないように考えられているようです。それは簡単です、

あなたは「未使用」のメモリを持っている場合:私は、しかし、それは実際にはほとんどの場合、最適化を防ぐことはできません、それについて考え

struct UnusedPadding { Empty e; Empty f; double d; int i; }; 
// chances are that the layout will leave some memory after int 

しかし、実際には、それも「悪いことです"それよりも、Emptyスペースは書かれていないので(あなたがEBOが始動するならば...)、したがって、あなたが実際に別のオブジェクトのアドレスではありません占有場所でそれを置くことができます:

struct Virtual { virtual ~Virtual() {} Empty e; Empty f; int i; }; 
// most compilers will reserve some space for a virtual pointer! 

か、でも私たちのオリジナルケースには:すべての私達場合

struct Foo { Empty e; int i; }; // deja vu! 

一つは(char*)foo.e == (char*)foo.i + 1を持つことができます別のアドレスが欲しかった。

+1

Boostの[Compressed Pair](http://www.boost.org/doc/libs/1_45_0/libs/utility/compressed_pa​​ir.htm)ライブラリを見て、この最適化の方法をご覧ください。 – GManNickG

+0

@GMan:彼らは巧みにEBOを使います。しかし、実際にこのEBOの使用はまさに私の質問から始まるものでした。 –

+0

これを参照してください:[Empty Base Optimization(EBO)を使用するプログラマはいつですか(0120)] – Nawaz

答えて

6

として-IFのルールの下で:

struct A { 
    EmptyThing x; 
    int y; 
}; 

A a; 
assert((void*)&(a.x) != (void*)&(a.y)); 

アサートがトリガされてはいけません。とにかく構造体にパディングを追加するだけでいい場合は、秘密にしてxのサイズを0にしても何のメリットもありません。

理論的には、コンパイラはポインタをメンバに渡すことができるかどうかを追跡し、そうでない場合にのみ最適化を行います。レイアウトが異なる2つの異なるバージョンの構造体があります:1つは最適化されたケース用で、もう1つは一般的なコード用です。

たとえば、スタックにAのインスタンスを作成し、完全にインライン化された(またはオプティマイザに表示される)インスタンスを作成すると、構造体の一部を完全に省略することができます。これは空のオブジェクトに固有のものではありませんが、空のオブジェクトはストレージにアクセスしないオブジェクトの特殊なケースに過ぎず、場合によっては全く割り当てられない場合もあります。

+0

はい、このアドレス識別の問題は、実際に私をここで悩ますものです。私はそれが使用できる可能性があると推測できるが、アドレスが取られていないオブジェクトは、使用されていないオブジェクト(その上のメソッドを呼び出すとそのアドレスが必要なため)になり、C++コンパイラがオブジェクトを実現する方法がわからないすべてのインラインクラスから離れて使用されていません。この非ゼロがなぜ使われているのか知っていますか? (つまり、それがCの残余者であるか、より現代的なスタイルで使用されている場合) –

+0

@Matthieu:完全なオブジェクトの非ゼロサイズは 'sizeof(array)/ sizeof(* array)'ゼロで割ることはありません。私はメンバーサブオブジェクトのサイズがゼロではないという強い根拠を考えることはできません。異なるアドレスのメンバオブジェクトが必要な場合、プログラマは 'EmptyThing'ではなく' char'型であることを確認できます。 。私は、特別なケースの処理が必要なオブジェクト/メモリモデルのどこかにいると思います。メンバのサブオブジェクトが明確で重複していないと仮定するのは簡単です。 –

+0

私は少し質問を更新しました。私は完全なオブジェクトのサイズがゼロでないことを要求していません(他の最適化のヒープを許してもいいですが、 'sizeof'もコンパイル時のコードなので、実行時のゼロ除算の心配はありません) 。 EBO(ゼロサイズの基底クラス)を持つことができれば、EDMO(ゼロサイズのデータ​​メンバー)を持つことができないのはなぜか分かりません。 –

2

技術的な理由から、空のクラスはサイズがゼロでなければなりません。
これは、別個のオブジェクトに異なるメモリアドレスがあることを強制するためです。したがって、コンパイラは静かに1バイトを空のオブジェクトに挿入します。
この制約は、自立型ではないため、派生クラスのベースクラス部分には適用されません。

-1

struct Empty { };を指定すると、sizeof(Empty) == 0の場合はどうなりますか。空オブジェクトのヒープを割り当てる一般的なコードは、たとえばrealloc(p, n * sizeof(T))のように、異なる動作をする可能性があります。ここでTEmptyで、これはfree(p)に相当します。 sizeof(Empty) != 0の場合、memset/memcpyなどのオブジェクトは、Emptyオブジェクトで使用されていないメモリ領域で動作しようとします。だから、コンパイラは、最終的な値の使用に基づいてsizeof(Empty)のようなものをステッチする必要があります - それは私には不可能に近く聞こえます。

現在のところ、C++ルールの下では、各メンバーが別個のアドレスを持つという保証は、それらのアドレスを使用してそれらのフィールドについての状態を符号化できることを意味します。フィールドオブジェクトのメンバ関数が訪問されるべきかどうかなどの情報が含まれます。アドレスが突然一致すると、これらのキーに依存する既存のコードが破損する可能性があります。

+0

memset/memcpyに間違った(つまり0以外の)長さを渡した場合のみ、 "memset/memcpyなどのようなものはEmptyオブジェクトによって使用されていないメモリ領域で動作しようとします" –

+0

@トニー:私は 'sizeof(Empty)'が '0'ではなく、' struct Foo {Empty e; int i; }; 'はsizeof(Foo)== sizeof(int)'を持っていたので、ここで約4/8バイトのメモリを節約することになります(整列のため) –

+0

@Steve:正確には---) - 文章は "sizeof Empty)!= 0 "、つまり、sizeof(T)'を使用してmemset/memcpy呼び出しのサイズを計算すると、TがEmptyの場合、ゼロ以外の値が返されます。 –

1

EmptyはPODタイプなので、memcpyを使用して「表現」を上書きすることができます。したがって、他のC++オブジェクトや有用なデータと共有しない方がよいでしょう。

関連する問題