2 つの既存のソリューションは、動的ポリモーフィズムを静的ポリモーフィズムと交換します。当面の問題の詳細がなければ、それが有効なアプローチであるかどうかを知ることはできません。これは基本的にポリモーフィック階層を壊すためです。CRTP では、単一の基本クラスではなく、それらのファミリーが存在します。Derived1
とのオブジェクトは無関係であるため、同じコンテナー内に保持することはできませんDerived2
... コードを共有することだけが必要な場合は優れたソリューションですが、動的なポリモーフィズムが必要な場合はそうではありません。同様の問題については、Visitor パターンと double-dispatch を参照してください。
動的ポリモーフィズムが必要な場合は、二重ディスパッチの実装を試みることができます (これは面倒ですが、階層が十分に小さければ実現可能です。基本的には、2 つの異なる階層を作成しますBase
。 rooted atBase
には仮想メソッドapply
があり、2 番目の階層には 1 番目の階層の型ごとに仮想関数があります。
class Base;
class Derived1; // inherits from Base, implements Visitor
class Derived2; // inherits from either Base or Derived2
struct Visitor {
virtual void visit( Base& ) = 0; // manually unrolled for all types
virtual void visit( Derived1& ) = 0;
virtual void visit( Derived2& ) = 0;
};
struct Base {
virtual void apply( Visitor& v ) { // manually replicate this in Derived1, 2
v.visit( *this );
}
template <typename T> void foo(T); // implement
};
template <typename T>
struct FooCaller : Visitor {
T& ref_value;
FooCaller( T& v ) : ref_value(v) {}
template <typename U> void call_foo( U& o ) {
o.foo(ref_value);
}
virtual void visit( Base & b ) { call_foo(b); }
virtual void visit( Derived1 & d1 ) { call_foo(d1); }
virtual void visit( Derived2 & d2 ) { call_foo(d2); }
};
私が使用した名前は Visitor パターンで一般的であり、このアプローチはそのパターンと非常によく似ています (あえて Visitor パターンとは呼びませんが、アプローチは似ているので、命名規則を借りただけです)。
ユーザーコードは次のようになります。
int main() // main returns int, not void!!!
{
Base* BasePtr = new Derived1();
int i = 5;
FooCaller<int> c(i)
BasePtr->apply(c); // [1] magic happens here
}
事前に宣言する必要性はi
、c
(可能であれば) 関数への引数を参照から const 参照に変更することで解放できます。実際の魔法は、[1] では C++ の単一ディスパッチ メカニズムが への呼び出しをディスパッチするDerived1::apply
ことです。これは、 が指すオブジェクトの動的な型であるためBasePtr
です。その時点でVisitor::visit( Derived1& )
、それ自体を引数として呼び出します。これは、単一のディスパッチ メカニズムを介して に再びディスパッチされFooCaller<int>::visit( Derived1& )
、その時点で、両方のオブジェクトが静的な型に解決されます。をFooCaller<int>::visit
呼び出すとcall_foo
、引数U
は であると推定されDerived1
、呼び出すとDerived1::foo
、引数は であると推定され、最終的にint
呼び出すことになりDerived1::foo<int>
ます...いくつかのループと間接的な方法があります...
特定のユース ケースによっては、これが複雑すぎる (CRTP のような静的ポリモーフィズムが機能する場合) か、保守が難しすぎる (階層が大きい場合: 階層内の新しい要素ごとに、階層内のすべての型Base
を更新する必要があります)。 ) したがって、この複雑さを回避できれば完璧です。ただし、場合によっては、これが必要になります。Visitor
また、これは最も複雑な完全に動的なソリューションであることに注意してください。実行時ポリモーフィズムである必要があるものに応じて、その間に他のオプションがあります...階層がショーツのビジターをモデル化している場合があります。内部でテンプレートにディスパッチされるさまざまな仮想関数を手動で展開するだけで済みます。その場合、上記の複雑さの半分は解消されます。
また、これはC ++では非常に珍しいことであり、実際の問題を説明すると、より簡単な解決策がある可能性があることに注意してください。あなたが述べたのは、元の問題に対する解決策の要件です。