空のベース最適化は素晴らしいです。ただし、次の制限があります。
同じ基本型の2つの基本サブオブジェクトはオブジェクト表現内で異なるアドレスを持つ必要があるため、空の基本クラスの1つが最初の非静的データメンバーの型の型または基本でもある場合、空の基本最適化は禁止されています。最も派生したタイプの。
この制限を説明するには、次のコードを検討してください。static_assert
失敗します。一方、どちらFoo
かBar
を変更するか、代わりにから継承するBase2
と、エラーが回避されます。
#include <cstddef>
struct Base {};
struct Base2 {};
struct Foo : Base {};
struct Bar : Base {
Foo foo;
};
static_assert(offsetof(Bar,foo)==0,"Error!");
私はこの振る舞いを完全に理解しています。私が理解していないのは、この特定の動作が存在する理由です。見落としではなく明示的な追加であるため、明らかに理由で追加されました。これの根拠は何ですか?
特に、2つの基本サブオブジェクトが異なるアドレスを持つ必要があるのはなぜですか?上記のBar
はタイプでfoo
あり、そのタイプのメンバー変数です。Bar
の基本クラスがのタイプの基本クラスと関係がある理由foo
、またはその逆の理由がわかりません。
実際、私はどちらかと言えば、それを含むインスタンス&foo
のアドレスと同じであることを期待しBar
ます。他の状況で必要になるためです(1)。結局のところ、私はvirtual
継承についてBase2
特別なことは何もしていません。基本クラスは関係なく空であり、を使用したコンパイルは、この特定のケースでは何も壊れないことを示しています。
しかし、明らかにこの推論はどういうわけか間違っており、この制限が必要になる他の状況があります。
答えがC ++ 11以降であるとしましょう(私は現在C ++ 17を使用しています)。
(1)注:EBOはC ++ 11でアップグレードされ、特にに対して必須になりましたStandardLayoutType
(ただしBar
、上記でははではありませんStandardLayoutType
)。
Base *a = new Bar(); Base *b = a->foo;
でa==b
はなく、a
かつb
明確に(おそらく別の仮想メソッドのオーバーライドと)異なるオブジェクトです。