1. ホーム
  2. c++

C++静的多相性(CRTP)と派生クラスからの型定義使用について

2023-11-28 21:33:24

質問

私は ウィキペディアの記事 を読んで、C++ で不思議なことに繰り返されるテンプレートパターンで静的な(コンパイル時の)ポリモーフィズムを実現することを知りました。私はこれを一般化して、関数の戻り値の型を派生型に基づいて変更できるようにしたいと思いました。(基本型はテンプレート・パラメータから派生型を知っているので、これは可能なはずです)。残念ながら、以下のコードはMSVC 2010でコンパイルできません(今、gccに簡単にアクセスできないので、まだ試していません)。どなたか理由をご存知ですか?

template <typename derived_t>
class base {
public:
    typedef typename derived_t::value_type value_type;
    value_type foo() {
        return static_cast<derived_t*>(this)->foo();
    }
};

template <typename T>
class derived : public base<derived<T> > {
public:
    typedef T value_type;
    value_type foo() {
        return T(); //return some T object (assumes T is default constructable)
    }
};

int main() {
    derived<int> a;
}

ところで、私は余分なテンプレートパラメータを使用して回避策を持っていますが、私はそれが好きではありません---それは、継承チェーンの上に多くの型を渡すときに非常に冗長になります。

template <typename derived_t, typename value_type>
class base { ... };

template <typename T>
class derived : public base<derived<T>,T> { ... };

EDITです。

このような状況でMSVC2010が出すエラーメッセージは error C2039: 'value_type' : is not a member of 'derived<T>'

g++ 4.1.2 (via codepad.org ) によると error: no type named 'value_type' in 'class derived<int>'

どのように解決するのですか?

derived のテンプレート引数として使用する場合、不完全です。 base へのテンプレート引数として使用すると、不完全です。

一般的な回避策は traits クラスのテンプレートを使用することです。 以下は、traits化したあなたの例です。 これは、派生クラスから型と関数の両方をtraitを通して使用する方法を示しています。

// Declare a base_traits traits class template:
template <typename derived_t> 
struct base_traits;

// Define the base class that uses the traits:
template <typename derived_t> 
struct base { 
    typedef typename base_traits<derived_t>::value_type value_type;
    value_type base_foo() {
        return base_traits<derived_t>::call_foo(static_cast<derived_t*>(this));
    }
};

// Define the derived class; it can use the traits too:
template <typename T>
struct derived : base<derived<T> > { 
    typedef typename base_traits<derived>::value_type value_type;

    value_type derived_foo() { 
        return value_type(); 
    }
};

// Declare and define a base_traits specialization for derived:
template <typename T> 
struct base_traits<derived<T> > {
    typedef T value_type;

    static value_type call_foo(derived<T>* x) { 
        return x->derived_foo(); 
    }
};

を特殊化するだけです。 base_traits を特殊化する必要があります。 derived_tbase のすべてのメンバを提供することを確認し、それぞれの特殊化が base が必要とするすべてのメンバーを提供するようにします。