Use the Curiously Recurring Template Pattern (CRTP) with additional type parameters

I am trying to use a Curiously Recurring Template Pattern (CRTP) and provide additional parameters like:

template <typename Subclass, typename Int, typename Float> class Base { Int *i; Float *f; }; ... class A : public Base<A, double, int> { }; 

This is probably a mistake, a more suitable superclass would be Base<A, double, int> - although this discrepancy in the order of the arguments is not so obvious. This error will be easier to see if I can use the name of the parameter value in typedef:

 template <typename Subclass> class Base { typename Subclass::Int_t *i; // error: invalid use of incomplete type 'class A' typename Subclass::Float_t *f; }; class A : public Base<A> { typedef double Int_t; // error: forward declaration of 'class A' typedef int Double_t; }; 

However, this does not compile on gcc 4.4, the reported errors are cited as comments above - I think the reason is that before creating A, he needs to create an instance of the base template, but in turn this should be known by A.

Is there a good way to pass "named" templates in parameters when using CRTP?

+7
source share
3 answers

You can use the feature class:

 // Must be specialized for any type used as TDerived in Base<TDerived>. // Each specialization must provide an IntType typedef and a FloatType typedef. template <typename TDerived> struct BaseTraits; template <typename TDerived> struct Base { typename BaseTraits<TDerived>::IntType *i; typename BaseTraits<TDerived>::FloatType *f; }; struct Derived; template <> struct BaseTraits<Derived> { typedef int IntType; typedef float FloatType; }; struct Derived : Base<Derived> { }; 
+19
source
Answer to

@James is clearly right, but you can still have some problems, however, if the user does not provide the correct typedefs.

It can be argued that the types used are correct using compile-time verification tools. Depending on the version of C ++ you are using, you may need to use Boost.

In C ++ 0x, this is done by combining:

  • static_assert : a new compile time checker that allows you to specify a message
  • a type_traits header that provides some predicates like std::is_integral or std::is_floating_point

Example:

 template <typename TDerived> struct Base { typedef typename BaseTraits<TDerived>::IntType IntType; typedef typename BaseTraits<TDerived>::FloatType FloatType; static_assert(std::is_integral<IntType>::value, "BaseTraits<TDerived>::IntType should have been an integral type"); static_assert(std::is_floating_point<FloatType>::value, "BaseTraits<TDerived>::FloatType should have been a floating point type"); }; 

This is very similar to the typical idioms of Defensive Programming in the run-time world.

+9
source

In fact, you do not even need feature classes. The following also works:

 template < typename T1, typename T2, template <typename, typename> class Derived_ > class Base { public: typedef T1 TypeOne; typedef T2 TypeTwo; typedef Derived_<T1, T2> DerivedType; }; template <typename T1, typename T2> class Derived : public Base<T1, T2, Derived> { public: typedef Base<T1, T2, Derived> BaseType; // or use T1 and T2 as you need it }; int main() { typedef Derived<int, float> MyDerivedType; MyDerivedType Test; return 0; } 
0
source

All Articles