2009-10-11 13 views
5

Considere un calendario de clase que almacena un grupo de objetos Date. El calendario está diseñado para contener una colección de cualquier tipo de objetos que hereden de Fecha. Pensé que la mejor manera de hacerlo es tener una plantilla de clase comoC++: plantilla de clase polimórfica

template<typename D> class Calendar{ 
    ... 
} 

Pero se me ocurrió que ahora D puede ser de hecho cualquier clase. Mi pregunta es ahora, ¿cómo puedo asegurarme de que D es una subclase del objeto de fecha?

Sé cómo hacer esto es Java, pero todavía no estoy familiarizado con la sintaxis de C++. El problema es muy similar a cómo algunas colecciones solo pueden tomar una plantilla de variables que implementen Comparable. El encabezado se vería más o menos como

public class Calendar<D extends Date>{ 
    ... 
} 

-------------------- EDIT: -------------- ----------------------------

El argumento de la plantilla define a qué día real se refiere el calendario. Los diferentes tipos de fecha se refieren al mismo día en diferentes formatos. Por ejemplo, si hago un Calendar<Gregorian> podrá tomar fechas en otro formato Date, decir el calendario juliano, o cualquier otro formato de fecha y presentarlos en formato gregoriano. Esto permite la conversión entre calendarios en diferentes formatos de fecha. Entonces, si tengo un Calendar<Gregorian> puedo convertirlo fácilmente en un Calendar<Julian>. A continuación, la siguiente es posible:

Calendar<Gregorian> cal; 
std::cout << "These events are entered as dates in 
    the Gregorian calendar" << std::endl; 
cal.add_event("Christmas", 12, 25); 
cal.add_event("Gregorian new year", 1, 1); 
std::cout << cal << std::endl; 
std::cout << "----" << std::endl; 
std::cout << "And printed out as Julian dates" << std::endl; 
Calendar<Julian>(cal); 
std::cout << cal<< std::endl; 

y salidas:

These events are entered as dates in the Gregorian calendar 
2009-12-25 Christmas 
2010-01-01 Gregorian new year 
---- 
And printed out as Julian dates 
2009-12-13 Christmas 
2009-12-19 Gregorian new year 

------------- Nueva edición: ----------- -----------

La última edición ahora tiene más sentido. Tenía un ligero desacuerdo con el formato.

Gracias por todas las respuestas.

Soy un estudiante de Informática en mi tercer año, y diría que estoy bastante familiarizado con OO y conceptos relacionados como Polimorfismo, etc. El propósito de este post fue averiguar si había o no un manera en C++ para expresar una condición para un argumento de plantilla de la misma manera que está en Java y resolver el problema de una manera concisa, elegante e intuitiva.

+2

¿Por qué * necesita * ser una subclase de Fecha? Siempre que se comporte como una Fecha (expone los miembros correctos), ¿qué hay de malo en tratarla como una Fecha? – jalf

Respuesta

9

Sé cómo hacer esto es Java, pero todavía no estoy familiarizado con la sintaxis de C++. El problema es muy similar a cómo algunas colecciones solo pueden tomar una plantilla de variables que implementen Comparable. La cabecera se parecería algo así como

public class Calendar<D extends Date>{ 
    ... 
} 

Es cierto, es el mismo problema, y ​​en C++, por lo general se resuelve por ignorarlo. ¿Por qué tenemos que hacer cumplir que el objeto debe implementar IComparable? En Java, es necesario debido a su sistema de tipo anémico. Sin esta restricción, no podríamos comparar objetos.

En C++, las reglas son diferentes. Los contenedores simplemente prueban para comparar los objetos que almacenan, y si el tipo no lo admite, se obtiene un error de compilación. No se requieren interfaces o herencia.

Y normalmente haría lo mismo en su clase Calendar. Simplemente no aplique la "subclase debe formar Date restricción.

En su lugar, especifique los miembros que el tipo debe exponer y, en su caso, qué semántica debería esperarse de ellos.

Por ejemplo, si el calendario de los intentos de realizar las siguientes operaciones, para la fecha de objetos d0 y d1:

d0.getDay(); 
d0.getTime(); 
Time t = d0 - d1; 

Entonces esas son las operaciones que deben ser apoyadas. Cualquier clase que admita estas operaciones es una clase de fecha válida, incluso si no incluye la subclase nada.

+0

IMO esa es la mejor respuesta hasta ahora. Ojalá mi respuesta hubiera dicho simplemente que la mecanografía pato está bien. +1 – sbi

+1

+1 para usar polimorfismo estático –

3

Creo que su problema se puede resolver sin usar plantillas. D siempre es una clase derivada de Fecha, entonces ¿por qué no solo tener una colección de objetos Date?

+0

En realidad, tengo curiosidad por la sintaxis, pero algunas funciones de la clase (irrelevantes para la pregunta) también requieren que tenga un tipo exacto. – Nubsis

0

Si solo desea interactuar con los objetos Date, ¿por qué no simplemente utilizar el polimorfismo simple y simplemente tratar con Date * -s?

Si planea tener una colección de Fechas en el Calendario, que puede contener instancias de diferentes subclases de Fecha, entonces dudo que las plantillas funcionen y usted no tiene nada más que el polimorfismo para ayudarlo en primer lugar.

En cuanto a las plantillas, si un tipo dado tiene una interfaz adecuada, ¿por qué no debería funcionar con el calendario? (Bueno, los conceptos se planificaron para C++ 0x, pero se descartaron, pero su principal motivación parecía ser permitir mensajes de error más claros relacionados con la plantilla.)

8

Lo que está buscando son verificaciones de conceptos para los argumentos de la plantilla. Estos habían sido parte del borrador para el próximo estándar de C++, pero habían sido descartados nuevamente hace algunas semanas/meses.

Sin conceptos en el lenguaje propiamente dicho, hay algunas bibliotecas que intentan hacer esto, pero la razón para querer que las verificaciones conceptuales sean parte del lenguaje central es que es más o menos imposible implementarlas sin soporte de idiomas.

En su ejemplo concreto, esto no debería ser demasiado difícil. Por ejemplo, usted podría poner un poco especial typedef en la clase base y comprobar si hay que:

class date { 
    public: 
    typedef int is_derived_from_date; 
}; 

template<typename D> class Calendar{ 
    typedef typename D::is_derived_from_date blah; 
    ... 
}; 

Otra forma sería la de elegir cualquiera de los is_derived<B,D>::result plantilla funciones meta que flotan alrededor en la red e implementar una comprobación estática para este en tu clase Calender. Boost tiene la función meta is_derived y una declaración estática.

Habiendo dicho todo esto, sin embargo, tengo que cuestionar su diseño. ¿Qué hay de malo con el polimorfismo OO ordinario en que quiera usar el polimorfismo en tiempo de compilación de las plantillas?

+1

Re. el último párrafo, ¿qué hay de malo con el polimorfismo en tiempo de compilación de las plantillas, que quiere usar el polimorfismo ordinario de OO? Estoy de acuerdo, la mezcla es incómoda. Según la información que tenemos, parece que tanto el polimorfismo dinámico como el estático podrían funcionar. Y entonces preferiría estática. – jalf

+0

@jalf: Yo también podría. Pero a juzgar por la pregunta, parece que Marco ya conoce bastante bien el polimorfismo OO, mientras que a IME le lleva bastante tiempo entender qué es lo que hacen las plantillas avanzadas y el polimorfismo estático. Entonces, para los principiantes en C++ que conocen OO, les sugiero que sigan así hasta que se sientan más familiarizados con la forma C++ de hacerlo. – sbi

+0

cierto. Cualquiera de los enfoques es válido. La ruta de la plantilla sería C++ más idiomática, creo, pero la de OO es más familiar para los principiantes y para los programadores que no usan C++. Es solo la mezcla de los dos lo que se vuelve problemático. – jalf

1

Las plantillas generalmente no requieren restricciones de herencia/polimorfismo. Una plantilla está diseñada para funcionar con cualquier tipo que satisfaga los requisitos establecidos independientemente de los tipos de base.

template <typename T> 
T clone(const T& cloneable) { 
    return cloneable.create_clone(); 
} 

Este código funcionará para cualquier tipo que tiene soporta una operación create_clone(), no se utiliza ningún ICloneable -interface!

En su caso, este código permitirá que se use cualquier tipo que se comporte como una fecha.

Si desea polimorfismo de clase base, simplemente deje las plantillas y use Date*.

Tenga en cuenta que si realmente desea hacer su prueba de plantilla, puede intentar lanzar un puntero de objeto ficticio a Date* que fallará en tiempo de compilación si no es derivado de Date. Pero esto normalmente no es la forma en que se usa el código de plantilla.

2

En C++ speak, esto se denomina comprobación de conceptos. En C++, una práctica recomendada común es que la herencia se usa para heredar interfaces y no para implementarla. Entonces, en realidad no le interesa tanto si D hereda de Date, pero si D tiene los elementos de interfaz de Date que necesita, es decir, tiene las funciones de miembro requeridas, etc. La ventaja de esto es que no necesita para tener futuras clases D que necesiten heredar de Date, solo necesitan implementar ciertas funciones.

La comprobación del concepto se eliminó en C++ 0x, pero puede encontrarla en Boost.ConceptCheck (el sitio principal de Boost es here).

Si realmente desea exigir que D hereda de Date sin embargo, se puede utilizar en combinación con Boost.StaticAssertBoost.TypeTraits para comprobar si D hereda de Date.

Cuestiones relacionadas