sencillos resueltos poo polimorfismo ejercicios ejemplos codigo clases c++ templates inheritance typedef crtp

resueltos - polimorfismo c++



Polimorfismo estático C++(CRTP) y uso de typedefs de clases derivadas (5)

En C ++ 14, puede eliminar el typedef y usar la deducción de tipo auto return return:

template <typename derived_t> class base { public: auto foo() { return static_cast<derived_t*>(this)->foo(); } };

Esto funciona porque la deducción del tipo de retorno de base::foo se retrasa hasta que se complete derived_t .

Leí el artículo de Wikipedia sobre el patrón de plantilla curiosamente recurrente en C ++ para hacer un polimorfismo estático (leer: tiempo de compilación). Quería generalizarlo para poder cambiar los tipos de devolución de las funciones en función del tipo derivado. (Parece que debería ser posible ya que el tipo de base conoce el tipo derivado del parámetro de la plantilla). Desafortunadamente, el siguiente código no se compilará utilizando MSVC 2010 (no tengo acceso fácil a gcc en este momento, así que aún no lo he probado). Alguien sabe por qué?

template <typename derived_t> class base { public: typedef typename derived_t::value_type value_type; value_type foo() { return static_cast<derived_t*>(this)->foo(); } }; template <typename T> class derived : public base<derived<T> > { public: typedef T value_type; value_type foo() { return T(); //return some T object (assumes T is default constructable) } }; int main() { derived<int> a; }

Por cierto, tengo una solución alternativa que utiliza parámetros de plantilla adicionales, pero no me gusta, se volverá muy detallado al pasar muchos tipos en la cadena de herencia.

template <typename derived_t, typename value_type> class base { ... }; template <typename T> class derived : public base<derived<T>,T> { ... };

EDITAR:

El mensaje de error que da MSVC 2010 en esta situación es el error C2039: ''value_type'' : is not a member of ''derived<T>''

g ++ 4.1.2 (a través de codepad.org ) dice error: no type named ''value_type'' in ''class derived<int>''


Puede evitar pasar 2 argumentos en la template . En CRTP, si tiene la seguridad de que class base se emparejará con la class derived (y no con la class derived_2 ), utilice la técnica siguiente:

template <typename T> class derived; // forward declare template <typename value_type, typename derived_t = derived<value_type> > class base { // make class derived as default argument value_type foo(); };

Uso:

template <typename T> class derived : public base<T> // directly use <T> for base


Un pequeño inconveniente de usar rasgos es que debe declarar uno para cada clase derivada. Puede escribir una solución menos detallada y continua como esta:

template <template <typename> class Derived, typename T> class base { public: typedef T value_type; value_type foo() { return static_cast<Derived<T>*>(this)->foo(); } }; template <typename T> class Derived : public base<Derived, T> { public: typedef T value_type; value_type foo() { return T(); //return some T object (assumes T is default constructable) } }; int main() { Derived<int> a; }


Una alternativa a los rasgos de tipo que requiere menos repetitivo es anidar su clase derivada dentro de una clase contenedora que contiene su typedefs (o use''s) y pasar el contenedor como un argumento de plantilla a su clase base.

template <typename Outer> struct base { using derived = typename Outer::derived; using value_type = typename Outer::value_type; value_type base_func(int x) { return static_cast<derived *>(this)->derived_func(x); } }; // outer holds our typedefs, derived does the rest template <typename T> struct outer { using value_type = T; struct derived : public base<outer> { // outer is now complete value_type derived_func(int x) { return 5 * x; } }; }; // If you want you can give it a better name template <typename T> using NicerName = typename outer<T>::derived; int main() { NicerName<long long> obj; return obj.base_func(5); }


derived está incompleta cuando la usa como un argumento de plantilla para base en su lista de clases base.

Una solución común es usar una plantilla de clase de rasgos. Aquí está tu ejemplo, traitsificado. Esto muestra cómo puede usar los tipos y funciones de la clase derivada a través de los rasgos.

// Declare a base_traits traits class template: template <typename derived_t> struct base_traits; // Define the base class that uses the traits: template <typename derived_t> struct base { typedef typename base_traits<derived_t>::value_type value_type; value_type base_foo() { return base_traits<derived_t>::call_foo(static_cast<derived_t*>(this)); } }; // Define the derived class; it can use the traits too: template <typename T> struct derived : base<derived<T> > { typedef typename base_traits<derived>::value_type value_type; value_type derived_foo() { return value_type(); } }; // Declare and define a base_traits specialization for derived: template <typename T> struct base_traits<derived<T> > { typedef T value_type; static value_type call_foo(derived<T>* x) { return x->derived_foo(); } };

Solo necesita especializar los base_traits para cualquier tipo que use para el argumento de la plantilla derived_t de la base y asegúrese de que cada especialización proporcione todos los miembros que la base requiera.