template plantillas c++ templates crtp

template - plantillas c++



¿Cómo escribir plantillas curiosamente recurrentes con más de 2 capas de herencia? (3)

Todo el material que he leído en Curiously Recurring Template Pattern parece una capa de herencia, es decir, Base y Derived : Base<Derived> . ¿Qué pasa si quiero dar un paso más?

#include <iostream> using std::cout; template<typename LowestDerivedClass> class A { public: LowestDerivedClass& get() { return *static_cast<LowestDerivedClass*>(this); } void print() { cout << "A/n"; } }; template<typename LowestDerivedClass> class B : public A<LowestDerivedClass> { public: void print() { cout << "B/n"; } }; class C : public B<C> { public: void print() { cout << "C/n"; } }; int main() { C c; c.get().print(); // B b; // Intentionally bad syntax, // b.get().print(); // to demonstrate what I''m trying to accomplish return 0; }

¿Cómo puedo reescribir este código para compilar sin errores y visualización?

do
segundo

Usando c.get (). Print () y b.get (). Print ()?

Motivación: supongamos que tengo tres clases,

class GuiElement { /* ... */ }; class Window : public GuiElement { /* ... */ }; class AlertBox : public Window { /* ... */ };

Cada clase toma aproximadamente 6 parámetros en su constructor, muchos de los cuales son opcionales y tienen valores predeterminados razonables. Para evitar el tedio de los parámetros opcionales , la mejor solución es utilizar el idioma de parámetros nominales .

Un problema fundamental de este modismo es que las funciones de la clase de parámetro tienen que devolver el objeto al que están llamadas, aunque algunos parámetros se asignan a GuiElement, otros a Window y algunos a AlertBox. Necesitas una forma de escribir esto:

AlertBox box = AlertBoxOptions() .GuiElementParameter(1) .WindowParameter(2) .AlertBoxParameter(3) .create();

Sin embargo, esto probablemente fallaría porque, por ejemplo, GuiElementParameter (int) probablemente devuelva GuiElementOptions &, que no tiene una función WindowParameter (int).

Esto se ha preguntado antes, y la solución parece ser un poco de sabor del Patrón de Plantilla Curiosamente Recurrente. El sabor particular que uso está aquí .

Sin embargo, es una gran cantidad de código para escribir cada vez que creo un nuevo Elemento Gui. He estado buscando formas de simplificarlo. Una causa principal de la complejidad es el hecho de que estoy usando CRTP para resolver el problema de Named-Parameter-Idiom, pero tengo tres capas, no dos (GuiElement, Window y AlertBox) y mi solución actual cuadruplica la cantidad de clases que tengo . (!) Por ejemplo, Window, WindowOptions, WindowBuilderT y WindowBuilder.

Eso me lleva a mi pregunta, en la que esencialmente estoy buscando una forma más elegante de usar CRTP en cadenas largas de herencia, como GuiElement, Window y Alertbox.


No estoy del todo claro sobre lo que espera lograr, pero esta es una aproximación cercana de lo que parece estar pidiendo.

template<typename LowestDerivedClass> class A { public: LowestDerivedClass& get() { return *static_cast<LowestDerivedClass*>(this); } void print() { cout << "A"; } }; template<typename LowestDerivedClass> class Bbase : public A<LowestDerivedClass> { public: void print() { cout << "B"; this->A<LowestDerivedClass>::print(); } }; class B : public Bbase<B> { }; class C : public Bbase<C> { public: void print() { cout << "C"; this->Bbase<C>::print(); } }; int main() { C c; c.print(); cout << endl; B b; b.print(); cout << endl; }

Cambié la salida para ilustrar mejor la herencia. En su código original, no puede pretender que B no es una plantilla [lo mejor que puede esperar es B<> ], por lo que algo como esto es probablemente la forma menos complicada de manejarlo.

De su otra respuesta, (2) no es posible. Puede dejar fuera los parámetros de la plantilla para las funciones, si los argumentos de la función son suficientes para inferirlos, pero con las clases debe proporcionar algo. (1) se puede hacer, pero es incómodo. Dejando todas las diversas capas:

template<typename T> struct DefaultTag { typedef T type; }; template<typename Derived = void> class B : public A<Derived> { /* what B should do when inherited from */ }; template<> class B<void> : public A<DefaultTag<B<void> > > { /* what B should do otherwise */ };

Tienes que hacer algo similar en cada nivel. Como dije, incómodo. No puede simplemente decir typename Derived = DefaultTag<B> > o algo similar porque B no existe todavía.


Aquí es en lo que me he basado, usando una variación en CRTP para resolver el problema presentado en mi ejemplo de motivación. Probablemente sea mejor leer comenzando desde abajo y desplazándose hacia arriba ...

#include "boost/smart_ptr.hpp" using namespace boost; // *** First, the groundwork.... // throw this code in a deep, dark place and never look at it again // // (scroll down for usage example) #define DefineBuilder(TYPE, BASE_TYPE) / template<typename TargetType, typename ReturnType> / class TemplatedBuilder<TYPE, TargetType, ReturnType> : public TemplatedBuilder<BASE_TYPE, TargetType, ReturnType> / { / protected: / TemplatedBuilder() {} / public: / Returns<ReturnType>::me; / Builds<TargetType>::options; / template<typename TargetType> class Builds { public: shared_ptr<TargetType> create() { shared_ptr<TargetType> target(new TargetType(options)); return target; } protected: Builds() {} typename TargetType::Options options; }; template<typename ReturnType> class Returns { protected: Returns() {} ReturnType& me() { return *static_cast<ReturnType*>(this); } }; template<typename Tag, typename TargetType, typename ReturnType> class TemplatedBuilder; template<typename TargetType> class Builder : public TemplatedBuilder<TargetType, TargetType, Builder<TargetType> > {}; struct InheritsNothing {}; template<typename TargetType, typename ReturnType> class TemplatedBuilder<InheritsNothing, TargetType, ReturnType> : public Builds<TargetType>, public Returns<ReturnType> { protected: TemplatedBuilder() {} }; // *** preparation for multiple layer CRTP example *** // // (keep scrolling...) class A { public: struct Options { int a1; char a2; }; protected: A(Options& o) : a1(o.a1), a2(o.a2) {} friend class Builds<A>; int a1; char a2; }; class B : public A { public: struct Options : public A::Options { int b1; char b2; }; protected: B(Options& o) : A(o), b1(o.b1), b2(o.b2) {} friend class Builds<B>; int b1; char b2; }; class C : public B { public: struct Options : public B::Options { int c1; char c2; }; private: C(Options& o) : B(o), c1(o.c1), c2(o.c2) {} friend class Builds<C>; int c1; char c2; }; // *** many layer CRTP example *** // DefineBuilder(A, InheritsNothing) ReturnType& a1(int i) { options.a1 = i; return me(); } ReturnType& a2(char c) { options.a2 = c; return me(); } }; DefineBuilder(B, A) ReturnType& b1(int i) { options.b1 = i; return me(); } ReturnType& b2(char c) { options.b2 = c; return me(); } }; DefineBuilder(C, B) ReturnType& c1(int i) { options.c1 = i; return me(); } ReturnType& c2(char c) { options.c2 = c; return me(); } }; // note that I could go on forever like this, // i.e. with DefineBuilder(D, C), and so on. // // ReturnType will always be the first parameter passed to DefineBuilder. // ie, in ''DefineBuilder(C, B)'', ReturnType will be C. // *** and finally, using many layer CRTP builders to construct objects ***/ int main() { shared_ptr<A> a = Builder<A>().a1(1).a2(''x'').create(); shared_ptr<B> b = Builder<B>().a1(1).b1(2).a2(''x'').b2(''y'').create(); shared_ptr<B> c = Builder<C>().c2(''z'').a1(1).b1(2).a2(''x'').c1(3).b2(''y'').create(); // (note: any order works) return 0; };


Creo que es imposible implementar algún mecanismo genérico. Debe especificar explícitamente el parámetro de plantilla exacto cada vez que hereda la clase base, sin importar cuántos niveles de direccionamiento indirecto se coloquen entre (a juzgar por su respuesta: ahora hay 2 niveles: no pasa C directamente a la base, pero C envuelto en una estructura de etiqueta, parece una serpiente que muerde su propia cola)

Probablemente, sería mejor para su tarea usar borrado de tipo y no un patrón de plantilla curiosamente recurrente. Puede ser, esto será útil