2012-05-15 12 views
6

この問題が説明するのは少し難しいですので、私は例から始めます:私はタイプとテンプレートパラメータとして一定の整数をとるクラステンプレートを持つテンプレートの特殊化は

、および

template <class V, int i> 
struct Base 
{ 
    static void doSomething() { cout << "something " << i << endl; }; 
}; 

struct Child : public Base<int,12> 
{ 
}; 

私はさまざまな種類の専門分野を持っているいくつかの他のテンプレート(のは、テストを呼びましょう)、とこれらのクラスを使用したい:私は、そのテンプレートのインスタンス化から派生した子クラスの数を持っています。 Baseのインスタンス化から派生したすべてのクラスで動作が完全に同じである必要があるため、Baseから派生したすべてのクラスを処理するTestの単一の特殊化を定義したいと考えています。

私はBase < V、i >に直接的に特化することはできませんが、これは子クラスを検出しないためです。代わりに、私の最初のアプローチは、ブーストのenable_ifとタイプ特性を使用していました:

// empty body to trigger compiler error for unsupported types 
template <class T, class Enabled = void> 
struct Test { }; 

// specialization for ints, 
// in my actual code, I have many more specializations here 
template <class Enabled> 
struct Test <int, Enabled> 
{ 
    static void test (int dst) 
    { 
     cout << "Test<int>::test(" << dst << ")" << endl; 
    } 
}; 

// this should handle all subclasses of Base, 
// but it doesn't compile 
template <class T, class V, int i> 
struct Test <T, typename enable_if <is_base_and_derived <Base <V,i>, T>>::type> 
{ 
    static void test (const T &dst) 
    { 
     dst.doSomething(); 
    } 
}; 

int main (int argc, char **argv) 
{ 
    Test <int>::test (23); 
    Test <Child>::test (Child()); 
    return 0; 
} 

アイデアは、専門のVとIの任意の値を持つ基地から派生するすべてのクラスを処理すべきであるということでした。これは動作しません、gccが文句を言う:私はこの問題は、このアプローチは、それらのいずれかが一致するかどうかを確認するためにVと私のすべての可能な組み合わせを試して、コンパイラが必要になるということである

 
error: template parameters not used in partial specialization: 
error:   ‘V’ 
error:   ‘i’ 

思います。

template <class V, int i> 
struct Base 
{ 
    typedef V VV; 
    static constexpr int ii = i; 
    static void doSomething() { cout << "something " << i << endl; }; 
}; 

この方法では、専門はもはやVを持っている必要がありますし、私のようなフリーのテンプレートパラメータ:

template <class T> 
struct Test <T, typename enable_if <is_base_and_derived <Base <typename T::VV, T::ii>, T>>::type> 
{ 
    static void test (const T &dst) 
    { 
     dst.doSomething(); 
    } 
}; 

そして今、私は、基本クラスに何かを追加することで問題を回避働いていましたそれはコンパイルする。

今、私の質問はです。どうすれば基本クラスを変更せずにこれを行うことができますか?この場合は自分で書いたので可能でしたが、そのようなテストテンプレートでサードパーティライブラリのコードを処理する必要がある場合はどうすればよいですか?より洗練されたソリューションはありますか?

編集:また、誰かが最初のアプローチがうまくいかない理由を詳細に説明できますか?私は大まかなアイデアを持っていますが、私は適切な理解を持つことを好むでしょう。 :-)

答えて

3

簡単な解決策はBaseは別のBase_baseを継承させることです。

struct Base_base 
{}; 

template <class V, int i> 
struct Base 
: public Base_base 
{ 
    static void doSomething() { cout << "something " << i << endl; }; 
}; 

template <class T> 
struct Test <T, typename enable_if <is_base_and_derived <Base_base, T>>::type> 
{ 
    static void test (const T &dst) 
    { 
     dst.doSomething(); 
    } 
}; 

[編集が]サードパーティのコードでは、あなたのようなトリックを使用することができます。

template <class V, int i> 
struct Base3rdparty 
{ 
    static void doSomething() { cout << "something " << i << endl; }; 
}; 

template <class V, int i> 
struct Base 
: public Base3rdparty<V, i> 
{ 
    typedef V VV; 
    static constexpr int ii = i; 
}; 

template <class T> 
struct Test <T, typename enable_if <is_base_and_derived <Base <typename T::VV, T::ii>, T>>::type> 
{ 
    static void test (const T &dst) 
    { 
     dst.doSomething(); 
    } 
}; 
+0

Base_Baseのおかげで、現在のコードを少し読みやすくなりました。しかし、サードパーティのライブラリの場合、これは動作しません。少なくとも、その子クラスもライブラリに属している場合はそうではありません。 –

+1

@BenjaminSchug:おそらく[this](http://stackoverflow.com/a/6398983/1324131)は、編集した質問の新しい質問に答えます。 – user2k5

+0

これは最初のアプローチがうまくいかなかった理由を説明しています。私の腸の感覚が正しいように見えます。 –

0

関数のオーバーロードを使用してdecltype

// Never defined: 
template<typename T> std::false_type is_Base(T&); 
template<class V, int I> std::true_type is_Base(Base<V,I>&); 

template<typename IsBase, typename T> struct TestHelper; 
template<typename T> struct TestHelper<std::true_type, T> 
{ 
    static void test(const T& dst) { dst.doSomething(); } 
}; 
template<> struct TestHelper<std::false_type, int> 
{ 
    static void test(int dst) 
    { std::cout << "Test<int>::test(" << dst << ")" << std::endl; } 
}; 
// ... 

template<typename T> struct Test 
{ 
    static void test(const T& dst) 
    { TestHelper<decltype(is_Base(std::declval<T&>())), T>::test(dst); } 
} 
関連する問題