Me gustaría tener varios tipos que compartan la misma implementación pero que sigan siendo de diferente tipo en C ++.
Para ilustrar mi pregunta con un ejemplo simple, me gustaría tener una clase para Manzanas, Naranjas y Plátanos, todas con las mismas operaciones y la misma implementación. Me gustaría que tuvieran diferentes tipos porque quiero evitar errores gracias a la seguridad de tipos.
class Apple {
int p;
public:
Apple (int p) : p(p) {}
int price () const {return p;}
}
class Banana {
int p;
public:
Banana (int p) : p(p) {}
int price () const {return p;}
}
class Orange ...
Para no duplicar el código, parece que podría usar una clase base Fruit y heredar de ella:
class Fruit {
int p;
public:
Fruit (int p) : p(p) {}
int price () const {return p;}
}
class Apple: public Fruit {};
class Banana: public Fruit {};
class Orange: public Fruit {};
Pero entonces, los constructores no se heredan y tengo que reescribirlos.
¿Existe algún mecanismo (typedefs, templates, herencia ...) que me permita tener fácilmente la misma clase con diferentes tipos?
Respuestas:
Una técnica común es tener una plantilla de clase donde el argumento de la plantilla simplemente sirve como un token único ("etiqueta") para convertirlo en un tipo único:
template <typename Tag> class Fruit { int p; public: Fruit(int p) : p(p) { } int price() const { return p; } }; using Apple = Fruit<struct AppleTag>; using Banana = Fruit<struct BananaTag>;
Tenga en cuenta que las clases de etiquetas ni siquiera necesitan estar definidas, es suficiente declarar un nombre de tipo único. Esto funciona porque la etiqueta no se usa en ninguna parte de la plantilla. Y puede declarar el nombre del tipo dentro de la lista de argumentos de la plantilla (sugerencia a @Xeo).
La
using
sintaxis es C ++ 11. Si está atascado con C ++ 03, escriba esto en su lugar:typedef Fruit<struct AppleTag> Apple;
Si la funcionalidad común requiere mucho código, desafortunadamente, esto introduce una gran cantidad de código duplicado en el ejecutable final. Esto se puede evitar teniendo una clase base común que implemente la funcionalidad y luego teniendo una especialización (que en realidad instancia) que se deriva de ella.
Desafortunadamente, eso requiere que vuelva a implementar todos los miembros no heredables (constructores, asignación…) lo que agrega una pequeña sobrecarga en sí misma, por lo que esto solo tiene sentido para clases grandes. Aquí se aplica al ejemplo anterior:
// Actual `Fruit` class remains unchanged, except for template declaration template <typename Tag, typename = Tag> class Fruit { /* unchanged */ }; template <typename T> class Fruit<T, T> : public Fruit<T, void> { public: // Should work but doesn’t on my compiler: //using Fruit<T, void>::Fruit; Fruit(int p) : Fruit<T, void>(p) { } }; using Apple = Fruit<struct AppleTag>; using Banana = Fruit<struct BananaTag>;
fuente
Fruit<struct SomeTag>
.Use plantillas y use un rasgo por fruta, por ejemplo:
struct AppleTraits { // define apple specific traits (say, static methods, types etc) static int colour = 0; }; struct OrangeTraits { // define orange specific traits (say, static methods, types etc) static int colour = 1; }; // etc
Luego tenga una sola
Fruit
clase que se escriba en este rasgo, por ejemplo.template <typename FruitTrait> struct Fruit { // All fruit methods... // Here return the colour from the traits class.. int colour() const { return FruitTrait::colour; } }; // Now use a few typedefs typedef Fruit<AppleTraits> Apple; typedef Fruit<OrangeTraits> Orange;
¡Puede ser un poco exagerado! ;)
fuente
template<class Derived> class Fruit;
fuente
También hay BOOST_STRONG_TYPEDEF .
fuente