より一般的には、これはテンプレートと継承全般に関する長年の問題です。
問題は、テンプレートが正確な型で機能し、継承係数を考慮しないことです。2 つの概念は多少直交しているため、一方と他方を混在させようとすると、エラーが発生しやすくなります。
メソッドでチェックアウトすることもできます:
template <class T>
int fooBar(T) { return 10; }
int fooBar(A) { return 20; }
B b;
fooBar(b); // this returns 10, because fooBar<T> is a better match (no conversion)
enable_if
さて、あなたの問題についてですが、とトリックを使用して与えられたさまざまな解決策に感謝しますが、is_base_of
それらは実用的ではないとして破棄します。専門化のポイントは、作成者がFoo
必要に応じて自分のクラスをどのように専門化するかを知る必要がなく、単に簡単にするためであるということです。そうでなければ、1ダースの専門化が必要な場合、非常に奇妙なFoo
クラスになってしまうことは間違いありません.
STL はすでに同様の問題を扱っています。一般に受け入れられているイディオムは、特性クラスを提供することです。デフォルトの特性クラスは、すべての人に適切なソリューションを提供しますが、特性クラスを特殊化してニーズに対応することもできます。
コンセプトを使用する方法があるはずだと思います (つまり、T が T::fooBar() を定義している場合はそれを使用し、そうでない場合はデフォルト バージョンを使用します...)、特定のメソッドのオーバーロードについては、これは必要ありません。
namespace detail { int fooBar(...) { return 10; } }
template <class T>
class Foo
{
public:
static int FooBar() { T* t(0); return ::detail::fooBar(t); }
};
そして今、A の派生クラスに特化するには:
namespace detail { int fooBar(A*) { return 20; } }
それはどのように機能しますか?オーバーロードを考慮する場合、省略記号は考慮される最後のメソッドであるため、前に修飾されたものはすべて実行されるため、デフォルトの動作に最適です。
いくつかの考慮事項:
名前空間: 識別子fooBar
が使用される可能性があるかどうかに応じて、独自の (またはFoo
クラス専用の) 名前空間に分離することを好む場合があります。そうでない場合は、非修飾呼び出しを行い、ユーザーに名前空間で定義させます彼女のクラス。
このトリックは、継承とメソッド呼び出しでのみ機能します。特別な typedef を導入したい場合は機能しません。
実際の型のように、より多くのテンプレートを実際のメソッドに渡すことができます
これはテンプレート関数の例です
namespace detail { template <class T> int fooBar(...) { return 10; } }
template <class T>
int Foo<T>::FooBar() { T* t(0); return ::detail::fooBar<T>(t); }
namespace detail {
template <class T>
int fooBar(A*)
{
return T::FooBar();
}
}
そして、ここで何が起こるか:
struct None {};
struct A { static int FooBar() { return 20; } };
struct B: A {};
struct C: A { static int FooBar() { return 30; } };
int main(int argc, char* argv[])
{
std::cout << Foo<None>::FooBar() // prints 10
<< " " << Foo<A>::FooBar() // prints 20
<< " " << Foo<B>::FooBar() // prints 20
<< " " << Foo<C>::FooBar() // prints 30
<< std::endl;
}