6

この問題は説明するのが少し難しいので、例から始めます。

テンプレート パラメーターとして型と整数定数を受け取るクラス テンプレートがあり、そのテンプレートのインスタンス化から派生する子クラスがいくつかあります。

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

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

これらのクラスを、さまざまなタイプに特化した他のテンプレート (Test と呼びましょう) で使用したいと考えています。Base のインスタンス化から派生したすべてのクラスの動作はまったく同じでなければならないため、Base から派生したすべてのクラスを処理する Test の特殊化を 1 つだけ定義したいと思います。

これは子クラスを検出しないため、 Base<V,i> を直接特化できないことはわかっています。代わりに、私の最初のアプローチは、Boost の 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 の任意の値を持つ Base から派生したすべてのクラスを特殊化で処理する必要があるという考えでした。これは機能しません、gcc は文句を言います:

エラー: 部分的な特殊化で使用されていないテンプレート パラメーター:
エラー: 'V'
エラー: 'i'

問題は、このアプローチでは、コンパイラーが V と i のすべての可能な組み合わせを試して、それらのいずれかが一致するかどうかを確認する必要があることだと思います。今のところ、基本クラスに何かを追加することで問題を回避しました。

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

このように、特殊化では V と 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();
    }
};

そして、コンパイルします。

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

編集:また、最初のアプローチが正確に機能しない理由を詳しく説明してもらえますか? 大まかな考えはありますが、きちんと理解した方がいいと思います。:-)

4

2 に答える 2

3

簡単な解決策は、 letBaseを継承すること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();
    }
};
于 2012-05-15T13:24:45.903 に答える
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); }
}
于 2012-05-15T14:06:22.253 に答える