皆さん、今晩は。
コード スニペットは 1,000 語の価値があります。
// Storage suitable for any of the listed instances
alignas(MaxAlign<Base, Derived1, Derived2>::value)
char storage[MaxSize<Base, Derived1, Derived2>::value];
// Instanciate one of the derived classes using placement new
new (storage) Derived2(3.14);
// Later...
// Recover a pointer to the base class
Base &ref = *reinterpret_cast<Base*> (storage);
// Use its (virtual) functions
ref.print();
// Destroy it when we're done.
ref.~Base();
ご覧のとおり、基本クラス ポインターを実際に格納せずに、基本クラスを介してのみインスタンスにアクセスしたいと考えています。2 番目の部分では、Derived2
型情報が失われることに注意してください。そのためstorage
、1 つの派生インスタンスが含まれているという保証のみが残されます。
配置 new は宛先ポインターを調整しないreinterpret_cast
ため、基本クラスにアップキャストするために使用することになります。static_cast
より適切な方がポインターを調整する場合があるため、これは危険であることがわかりました。[1]
実際、未定義の動作を引き起こします。完全なコードはここ Coliru (g++ 4.9.0) にあります。実行時にすぐにクラッシュします。一方、私の PC (g++ 4.8.2) では、すべて問題ありません。g++ 4.9 では、関数を呼び出す前に両方のポインターを出力すると、同じ値が表示され、機能することに注意してください。
そこで、私は問題を逆に考えようとしました: へのポインターBase
が と等しくなるように、派生インスタンスを微調整しましたstorage
。
void *ptr = static_cast<Derived2*>(reinterpret_cast<Base*>(storage));
new (ptr) Derived2(3.14);
運がない。g++ 4.8 では問題なく動作しますが、g++ 4.9 では問題が発生します。
編集:考えてみると、上記はそれほどスマートではありません。なぜなら、派生インスタンスが前に storage
終わったらどうなるか... 痛い。
だから私の質問は:私が達成しようとしていることに対する解決策はありますか? または、[1] で言及されているケースは、クラスのサブセット (仮想継承がないなど) で動作するコードを記述できるほど十分に明確に定義されていますか?