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

只谈情不闲聊 提交于 2019-12-20 12:41:11

问题


I try to use the Curiously Recurring Template Pattern (CRTP) and provide additional type parameters:

template <typename Subclass, typename Int, typename Float>
class Base {
    Int *i;
    Float *f;
};
...

class A : public Base<A, double, int> {
};

This is probably a bug, the more appropriate superclass would be Base<A, double, int> -- although this argument order mismatch is not so obvious to spot. This bug would be easier to see if I could use name the meaning of the parameters in a 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 given as comments above -- I think the reason is that before creating A, it needs to instantiate the Base template, but this in turn would need to know A.

Is there a good way of passing in "named" template parameters while using CRTP?


回答1:


You can use a traits 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> 
{
};



回答2:


@James answer is obviously right, but you could still have some issues nonetheless, if the user does not provide correct typedefs.

It is possible to "assert" that the types used are right using compile-time checking facilities. Depending on the version of C++ you use, you could have to use Boost.

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

  • static_assert: a new facility for compile-time checking, which let's you specify a message
  • the type_traits header, which 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 typical Defensive Programming idioms in the runtime world.




回答3:


You actually don't even need the traits 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;
}


来源:https://stackoverflow.com/questions/5680263/use-curiously-recurring-template-pattern-crtp-with-additional-type-parameters

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!