CRTPまたはコンパイル時のポリモーフィズムは、コンパイル時にすべての型を知っている場合に使用します。addWidget
実行時にウィジェットのリストを収集するために使用している限りfooAll
、実行時barAll
にその同種のウィジェットのリストのメンバーを処理する必要がある限り、実行時にさまざまなタイプを処理できる必要があります。したがって、あなたが提示した問題については、ランタイムポリモーフィズムを使用して立ち往生していると思います。
もちろん、標準的な答えは、実行時のポリモーフィズムのパフォーマンスが問題であることを確認してから、それを回避しようとすることです...
実行時の多形性を本当に回避する必要がある場合は、次のいずれかの解決策が機能する可能性があります。
オプション1:ウィジェットのコンパイル時コレクションを使用する
WidgetCollectionのメンバーがコンパイル時にわかっている場合は、テンプレートを非常に簡単に使用できます。
template<typename F>
void WidgetCollection(F functor)
{
functor(widgetA);
functor(widgetB);
functor(widgetC);
}
// Make Foo a functor that's specialized as needed, then...
void FooAll()
{
WidgetCollection(Foo);
}
オプション2:ランタイムポリモーフィズムを無料の関数に置き換えます
class AbstractWidget {
public:
virtual AbstractWidget() {}
// (other virtual methods)
};
class WidgetCollection {
private:
vector<AbstractWidget*> defaultFooableWidgets;
vector<AbstractWidget*> customFooableWidgets1;
vector<AbstractWidget*> customFooableWidgets2;
public:
void addWidget(AbstractWidget* widget) {
// decide which FooableWidgets list to push widget onto
}
void fooAll() {
for (unsigned int i = 0; i < defaultFooableWidgets.size(); i++) {
defaultFoo(defaultFooableWidgets[i]);
}
for (unsigned int i = 0; i < customFooableWidgets1.size(); i++) {
customFoo1(customFooableWidgets1[i]);
}
for (unsigned int i = 0; i < customFooableWidgets2.size(); i++) {
customFoo2(customFooableWidgets2[i]);
}
}
};
醜い、そして実際にはOOではありません。テンプレートは、すべての特殊なケースをリストする必要性を減らすことで、これを支援することができます。次のようなものを試してください(完全にテストされていません)が、この場合はインライン化されていません。
class AbstractWidget {
public:
virtual AbstractWidget() {}
};
class WidgetCollection {
private:
map<void(AbstractWidget*), vector<AbstractWidget*> > fooWidgets;
public:
template<typename T>
void addWidget(T* widget) {
fooWidgets[TemplateSpecializationFunctionGivingWhichFooToUse<widget>()].push_back(widget);
}
void fooAll() {
for (map<void(AbstractWidget*), vector<AbstractWidget*> >::const_iterator i = fooWidgets.begin(); i != fooWidgets.end(); i++) {
for (unsigned int j = 0; j < i->second.size(); j++) {
(*i->first)(i->second[j]);
}
}
}
};
オプション3:OOを排除する
OOは、複雑さを管理し、変化に直面しても安定性を維持するのに役立つため、便利です。あなたが説明しているように見える状況(その振る舞いは一般に変わらず、メンバーメソッドは非常に単純な何千ものウィジェット)では、管理するのにそれほど複雑さや変更がない場合があります。その場合は、OOは必要ないかもしれません。
このソリューションは、「仮想」メソッドと既知のサブクラス(OOではない)の静的リストを維持する必要があり、仮想関数呼び出しをインライン関数へのジャンプテーブルに置き換えることができることを除いて、ランタイムポリモーフィズムと同じです。
class AbstractWidget {
public:
enum WidgetType { CONCRETE_1, CONCRETE_2 };
WidgetType type;
};
class WidgetCollection {
private:
vector<AbstractWidget*> mWidgets;
public:
void addWidget(AbstractWidget* widget) {
widgets.push_back(widget);
}
void fooAll() {
for (unsigned int i = 0; i < widgets.size(); i++) {
switch(widgets[i]->type) {
// insert handling (such as calls to inline free functions) here
}
}
}
};