この問題に対してエレガントな解決策が存在するかどうか疑問に思いました。
次のように仮定します。
class Base {
private:
int data;
protected:
Base(int data) : data(data) { }
int getData() const { return data; }
virtual bool someOp() = 0;
}
class Derived1 : public Base {
private:
int data2;
public:
Derived1(int data, int data2) : Base(data), data2(data2) { }
int getData2() const { return data2; }
bool someOp() override { /* something */ }
}
class Derived2 : public Base {
private:
float data2;
public:
Derived1(int data, float data2) : Base(data), data2(data2) { }
float getData2() const { return data2; }
bool someOp() override { /* something */ }
}
そして、階層を完全に制御できるので、階層Base
もDerivedX
クラスも拡張されないと想定できます。
今、私はそれらをに保存しstd::vector
たいと思います。ポリモーフィズムを使用したい場合は、ポインターを保存する必要があります。そうしないと、オブジェクトのスライスによって追加の派生プロパティを保存できなくなります。だから私は基本的にを使用することを余儀なくされていますstd::vector<unique_ptr<Base>>
。
しかし、これらのオブジェクトをたくさん格納する必要があり、ダブルヒープ割り当て(内部std::vector
+オブジェクト自体)に無駄にしたくないと仮定しましょう。同時に、次のことを仮定できます。
sizeof(DerivedX)
より大きくなることはありません sizeof(Base)
したがって、ポリモーフィズムを維持し、ポインターの格納を回避するためのエレガントな方法があるかどうか疑問に思っています。私は次のようないくつかの解決策を考えることができます
class Base {
enum {
DERIVED1,
DERIVED2
} type;
int data;
union {
int derived1data;
float derived2data;
}
bool someOp() {
if (this->type == DERIVED1) ...
else if ..
}
}
しかし、これは明らかにエレガントではありません。でsizeof(Derived) == sizeof(Base)
保護されたユニオンを使用し、Base
そこからアクセスしてDerived
、アドレスをstd::vector
目的のタイプの要素にキャストすることで、オブジェクトのスライスが発生しないという事実を利用することもできますが(列挙型による)、これも醜いようです。
型消去と小さなバッファーの最適化。
C ++のほとんどすべてのプロパティを消去と入力して、プロパティを現在不明な型に適用する方法を「知っている」カスタムインターフェイスを作成できます。
boost::any
typeは、コピー、破棄、typeidの取得、および完全に一致するtypeへのキャストバックのために消去されます。std::function
タイプは消去してコピーし、特定の署名で呼び出し、破棄し、同一タイプにキャストバックします(最後のものはめったに使用されません)。
無料のストアベースの型消去実装は、ポインタを交換することにより、「無料で」移動セマンティクスを取得します。
あなたの場合、そのタイプに「十分な大きさ」の整列されたストレージを作成する必要があります。コピー、ベースへの参照として取得、破棄、そしておそらく移動するには、erase downと入力する必要があります(内部に保存しているため)。
std::aligned_storage
あなたのタスクを対象としています(保存しようとしているすべてのタイプの配置要件を渡すことができます)。次に、新しいオブジェクトをインプレースします。
void*
コピー、移動、破棄、および変換を介して、オブジェクトに対して実行する操作のテーブルを作成しますbase*
。
template<class Sig>using f = Sig*;
struct table {
f<base*(void*)> convert_to_base;
f<base const*(void const*)> convert_to_const_base;
f<void(void*,void const*)> copy_construct;
f<void(void*)> destroy;
f<void(void*,void*)> move_construct;
};
template<class T>
table const* get_table() {
static table t = {
// convert to base:
[](void*p)->base*{
T*t=static_cast<T*>(p);
return t;
},
// convert to const base:
[](void const*p)->base const*{
T const*t=static_cast<T const*>(p);
return t;
},
// etc
};
return &t;
}
今保存するget_table<T>()
(それは手動で実装、基本的に仮想関数テーブルである)あなたのタイプ消去されたインスタンスでは、との関数を使用するようにラッピング定期的なクラスを作成table
、操作しますaligned_storage<?...>
。
さて、これは、を使用するか、ヒープストレージを使用せずにのように動作するboost::variant
私のタイプのようなものを介して簡単に行うことができますany
。一部のリンクには、上記の疑似仮想関数テーブル手法をコンパイルする実装が含まれています。整列ストレージを間違って使用した可能性がありますが、注意してください。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加