问题
In the following code:
template <typename T>
class CRTP
{
public:
};
template <int I, typename T>
class CRTPInt
{
public:
};
template <template <typename> class T>
class Derived : public T<Derived<T>>
{
public:
};
void main()
{
Derived<CRTP> foo;
Derived<CRTPInt<2>> foo2;
}
How do I write CRPTInt so I can pass in a templatized parameter that will then be continued in the Derived definition?
Thanks,
Jim
回答1:
The CRTP pattern is typically used to enable static polymorphism and the ability to mixin (parametrized) behavior. To illustrate two alternatives, it's convenient to first define a general template
template
<
typename Derived
>
class enable_down_cast
{
private:
// typedefs
typedef enable_down_cast Base;
public:
Derived const* self() const
{
// casting "down" the inheritance hierarchy
return static_cast<Derived const*>(this);
}
// write the non-const version in terms of the const version
// Effective C++ 3rd ed., Item 3 (p. 24-25)
Derived* self()
{
return const_cast<Derived*>(static_cast<Base const*>(this)->self());
}
protected:
// disable deletion of Derived* through Base*
// enable deletion of Base* through Derived*
~enable_down_cast() = default; // C++11 only, use ~enable_down_cast() {} in C++98
};
Then you define an interface class template for the type of behavior that you want
template<typename FX>
class FooInterface
:
// enable static polymorphism
public enable_down_cast< FX >
{
private:
// dependent name now in scope
using enable_down_cast< FX >::self;
public:
// interface
void foo() { self()->do_foo(); }
protected:
// disable deletion of Derived* through Base*
// enable deletion of Base* through Derived*
~IFooInterface() = default; // C++11 only, use ~IFooInterface() {} in C++98/03
};
To get different implementations of this interface, simply define different classes that each derive from FooInterface
with themselves as curiously recurring template parameters:
class FooImpl
:
public FooInterface< FooImpl >
{
private:
// implementation
friend class FooInterface< FooImpl > ;
void do_foo() { std::cout << "Foo\n"; }
};
class AnotherFooImpl
:
public FooInterface< AnotherFooImpl >
{
private:
// implementation
friend class FooInterface< AnotherFooImpl >;
void do_foo() { std::cout << "AnotherFoo\n"; }
};
The alternative is to parametrize the different implementations of an interface. This time, the class template depends on both a template-template parameter and a non-type parameter
template<template<int> class F, int X>
class BarInterface
:
public enable_down_cast< F<X> >
{
private:
// dependent name now in scope
using enable_down_cast< F<X> >::self;
public:
// interface
void bar() { self()->do_bar(); }
protected:
// disable deletion of Derived* through Base*
// enable deletion of Base* through Derived*
~BarInterface() = default; // C++11 only, use ~BarInterface() {} in C++98/03
};
The implementation is then another class template, which derives from the interface with both itself and the non-type parameter as arguments
template< int X >
class BarImpl
:
public BarInterface< BarImpl, X >
{
private:
// implementation
friend class BarInterface< ::BarImpl, X >;
void do_bar() { std::cout << X << "\n"; }
};
This is how you call them:
int main()
{
FooImpl f1;
AnotherFooImpl f2;
BarImpl< 1 > b1;
BarImpl< 2 > b2;
f1.foo();
f2.foo();
b1.bar();
b2.bar();
return 0;
}
The classes in your question don't quite fit into this general pattern. If you might want to give Derived
some CRTP-like behavior, then you can either do
class Derived1
:
public CRTP< Derived1 >
{
};
template<int I>
class Derived2
:
public CRTPInt< Derived2, I >
{
};
UPDATE: Based on the discussion from https://stackoverflow.com/a/11571808/819272, I discovered that the original answer only compiled on Visual Studio 2010, but not on gcc because of some Microsoft-specific, non-portable features. E.g. the self()
function from enable_down_cast
is a (template) dependent name in its derived classes, and therefore not visible without explicit using
directives. Furthermore, I have added defaulted destructors with the right level of protection. Finally, I have renamed my original class enable_crtp
to enable_down_cast
because that is precisely what it does: manually enable for static polymporphism what the compiler does automatically for dynamic polymorphism.
来源:https://stackoverflow.com/questions/11546478/how-do-i-pass-template-parameters-to-a-crtp