问题
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 likestd::is_integral
orstd::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