2009-02-20 19 views
13

Estoy intentando declarar una y una clase Column, con el tener un std::map privado con los valores que apuntan a una plantilla Column. Algo como esto:C++ std :: mapa de clase plantilla valora

template <typename T> 
class DataType { 
    private: 
    T type; 
}; 
template <typename T> 
class Field { 
    private: 
    T value; 
    DataType<T> value; 
}; 
class Row { 
    private: 
    std::map<unsigned long,Field*> column; 
}; 

Bueno, supongo que, en principio, la clase no debería tener que saber qué tipo de Field (o Column) nos gustaría utilizar, es decir, si se trata de un Field<int> en la columna 1 o a Field<double> en la columna 2. Pero no estoy seguro de cuál es la sintaxis correcta para la declaración Row::column, o si el std::map está limitado en este sentido y debería estar usando algo más.

Agradezco sus sugerencias y les agradezco de antemano.

+0

¿Cuál es la pregunta? –

+1

no tiene que transformar su código en html. simplemente póngalo como está, con una sangría de 4 caracteres. –

+0

a Dave: Mi pregunta es: ya que el campo está modelado, ¿cómo puedo "decirle" al archivo std :: que los valores son "cualquier tipo de campo"? a litb: Gracias por la sugerencia! :-) – jbatista

Respuesta

22

Field solo no es un tipo, sino una plantilla que puede generar una familia de tipos, como Field<int> y Field<double>. Todos estos campos no están relacionados de modo que uno se deriva de alguna manera del otro o tal. Entonces debes establecer alguna relación entre todos estos tipos generados. Una forma es utilizar una plantilla de no-clase base común:

class FieldBase { }; 

template <typename T> 
class Field : public FieldBase { 
    private: 
    T value; 
    DataType<T> type; 
}; 
class Row { 
    private: 
    std::map<unsigned long,FieldBase*> column; 
}; 

Y considerar el uso de puntero inteligente en lugar de ese puntero prima en el código. De todos modos, ahora el problema es que la información de tipo se pierde, ya sea que apunte a Field<double> o a Field<int> ya no se conoce y solo se puede detectar manteniendo algún tipo de indicador de tipo en la base establecida por la plantilla clase derivada - o preguntando RTTI usando

dynamic_cast<Field<int>*>(field) != 0 

Pero eso es feo. Especialmente porque lo que quieres allí es un valor semántico. Es decir, desearía poder copiar su fila y copiaría todos los campos en ella. Y desea obtener un doble cuando se almacena un doble, sin usar RTTI primero para abrirse camino hacia el tipo derivado.

Una forma de hacerlo es usar una unión discriminada. Eso es básicamente una unión para algunos tipos arbitrarios y, además, un indicador de tipo, que almacena qué valor está almacenado actualmente en ese campo (por ejemplo, si es doble, int, ...). Por ejemplo:

template <typename T> 
class Field { 
    private: 
    T value; 
    DataType<T> type; 
}; 
class Row { 
    private: 
    std::map<unsigned long, 
      boost::variant< Field<int>, Field<double> > > 
     column; 
}; 

boost :: variant hace todo el trabajo por usted. Puede usar las visitas para hacer que se llame a un functor usando la sobrecarga correcta.Eche un vistazo a su manual

1
  1. Tienes un error allí: tienes que "valorar" el miembro en el campo (uno debería ser probablemente "tipo").
  2. No guarde los punteros sin formato en el valor del mapa. Use boost::shared_ptr.
  3. Además, debe tener una buena razón para escribir estas clases donde ya hay muchos códigos de manejo de BD/tablas que probablemente pueda usar. Entonces, si es aplicable, considere usar algo existente y no escribir su propio código de manejo de tabla.

Ahora, para responder a su pregunta :), las clases de Campo <> pueden heredar de una clase base común que sea compartida por todos los tipos de datos. De esta forma, un contenedor como el mapa de columnas puede mantener los punteros (haga que punteros compartidos) a los objetos derivados que se instancian de una clase de plantilla.

+0

1. Lo siento, tienes razón, quise decir: T avalue; DataType atype; 2. No estoy familiarizado con la biblioteca de Boost (supongo que de eso es de lo que estás hablando), pero la examinaré, gracias. 3. ¿Podría indicarme una o dos sugerencias que podría analizar? – jbatista

0

A Row< int, float, int> es realmente diferente de Row<int, std::string>. Claramente, Row<int,float,int>.field<0> debe ser un Field<int>, mientras que Row<int,float,int>.field<1> debe ser un Field<float>. Y Row<int,float,int>.field<3> es un error del compilador.

La forma más fácil de hacerlo es usando Boost. Una gran parte de la inteligencia fue iniciada por Loki (ver Modern C++ Design, by Andrei Alexandrescu) pero Boost es más moderno y está mejor respaldado.

Normalmente, no iteraría sobre los campos; cada campo tiene su propio tipo. Pero de ti, de hecho, necesitarías un FieldBase. Si necesita una interfaz de este tipo, probablemente también valga la pena almacenar los campos internamente como boost::array<FieldBase, N> (es decir, Row<int,float,int> tiene boost::array<FieldBase, 3>). Sin embargo, nunca debería necesitar dynamic_cast que FieldBase*. Esa es una prueba de tiempo de ejecución, y usted siempre sabe exactamente el T de cada Field<T> en tiempo de compilación.

Cuestiones relacionadas