template sirven que programar plantillas plantilla para los funcion ejemplos con c++ templates crtp

c++ - sirven - programar con plantillas



¿Cómo paso los parámetros de la plantilla a un CRTP? (1)

En el siguiente código:

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; }

¿Cómo escribo CRPTInt para poder pasar un parámetro de templado que luego se continuará en la definición derivada?

Gracias,

Jim


El patrón de CRTP se usa generalmente para permitir el polimorfismo estático y la capacidad de mezclar (parametrizar) el comportamiento. Para ilustrar dos alternativas, es conveniente definir primero una plantilla general

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 };

A continuación, define una plantilla de clase de interfaz para el tipo de comportamiento que desea

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 };

Para obtener diferentes implementaciones de esta interfaz, simplemente defina las diferentes clases que cada una deriva de FooInterface consigo mismas como parámetros de plantilla curiosamente recurrentes :

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"; } };

La alternativa es parametrizar las diferentes implementaciones de una interfaz. Esta vez, la plantilla de clase depende tanto de un parámetro plantilla-plantilla como de un parámetro no-tipo

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 };

La implementación es entonces otra plantilla de clase, que se deriva de la interfaz tanto con el parámetro no-type como con los argumentos

template< int X > class BarImpl : public BarInterface< BarImpl, X > { private: // implementation friend class BarInterface< ::BarImpl, X >; void do_bar() { std::cout << X << "/n"; } };

Así es como los llamas:

int main() { FooImpl f1; AnotherFooImpl f2; BarImpl< 1 > b1; BarImpl< 2 > b2; f1.foo(); f2.foo(); b1.bar(); b2.bar(); return 0; }

Las clases en su pregunta no encajan en este patrón general. Si desea darle a Derived algún comportamiento similar al de CRTP, entonces puede hacerlo

class Derived1 : public CRTP< Derived1 > { }; template<int I> class Derived2 : public CRTPInt< Derived2, I > { };

ACTUALIZACIÓN : Basado en la discusión de https://.com/a/11571808/819272 , descubrí que la respuesta original solo se compilaba en Visual Studio 2010, pero no en gcc debido a algunas características no portátiles específicas de Microsoft. Por ejemplo, la función self() de enable_down_cast es un nombre dependiente (plantilla) en sus clases derivadas, y por lo tanto no es visible sin directivas explícitas. Además, he agregado destructores con el nivel correcto de protección. Finalmente, he cambiado el nombre de mi clase original enable_crtp por enable_down_cast porque eso es precisamente lo que hace: habilitar manualmente para el polimorfismo estático lo que el compilador hace automáticamente para el polimorfismo dinámico.