2008-12-29 18 views
7

¿Cuál es la mejor manera de tener una matriz asociativa con tipos de valores arbitrarios para cada clave en C++?matriz asociativa C++ con tipos arbitrarios para los valores

Actualmente mi plan es crear una clase de "valor" con variables miembro de los tipos que esperaré. Por ejemplo:

class Value { 

    int iValue; 
    Value(int v) { iValue = v; } 

    std::string sValue; 
    Value(std::string v) { sValue = v; } 

    SomeClass *cValue; 
    Value(SomeClass *v) { cValue = c; } 

}; 

std::map<std::string, Value> table; 

Un inconveniente con esto es que usted tiene que saber el tipo cuando se accede al "Valor". es decir .:

table["something"] = Value(5); 
SomeClass *s = table["something"].cValue; // broken pointer 

también los más tipos que se ponen en valor, el más hinchado de la matriz será.

¿Alguna mejor sugerencia?

+0

BTW, a std :: map no es una tabla hash; generalmente se implementa como un árbol rojo-negro, pero en cualquier caso, las claves se mantienen en orden. Hay std :: tr1 :: unordered_map, que generalmente se implementa como una tabla hash. –

+0

Chris, gracias por señalar eso. Ahora lo estoy llamando una "matriz asociativa". –

+0

relacionado: ["¿Cuál es el punto de las matrices heterogéneas?"] (Http://stackoverflow.com/questions/4534612/what-is-the-point-of-heterogenous-arrays) –

Respuesta

2

Subclase Value con IntValue, StringValue, y así sucesivamente.

9

Su enfoque fue básicamente en la dirección correcta. Usted tiene que saber el tipo que ha puesto. Puede utilizar boost::any y usted será capaz de poner casi cualquier cosa en el mapa, como siempre que se sepa lo que pone en:

std::map<std::string, boost::any> table; 
table["hello"] = 10; 
std::cout << boost::any_cast<int>(table["hello"]); // outputs 10 

Algunas respuestas recomienda el uso de boost::variant para resolver este problema. Pero no le permitirá almacenar valores tipeados arbitrarios en el mapa (como quería). Debe conocer el conjunto de tipos posibles de antemano. Teniendo en cuenta que, puede hacer lo anterior con mayor facilidad:

typedef boost::variant<int, std::string, void*> variant_type; 
std::map<std::string, variant_type> table; 
table["hello"] = 10; 
// outputs 10. we don't have to know the type last assigned to the variant 
// but the variant keeps track of it internally. 
std::cout << table["hello"]; 

Eso funciona porque boost::variant sobrecargas operator<< para ese propósito. Es importante entender que si desea guardar lo que está contenido actualmente en la variante, que todavía tiene que conocer el tipo, al igual que en el caso boost::any:

typedef boost::variant<int, std::string, void*> variant_type; 
std::map<std::string, variant_type> table; 
table["hello"] = "bar"; 
std::string value = boost::get<std::string>(table["hello"]); 

El orden de las asignaciones a una variante es un tiempo de ejecución propiedad del flujo de control de su código, pero el tipo utilizado de cualquier variable se determina en tiempo de compilación. Entonces, si desea obtener el valor de la variante, debe conocer su tipo. Una alternativa es usar las visitas, tal como se describe en la documentación variante. Funciona porque la variante almacena un código que le indica qué tipo se le asignó por última vez. Basado en eso, decide en el tiempo de ejecución que sobrecarga del visitante que usa. boost::variant es bastante grande y no cumple completamente con las normas, mientras que boost::any cumple con las normas pero utiliza memoria dinámica incluso para tipos pequeños (por lo que es más lenta. La variante puede usar la pila para tipos pequeños). Entonces debes intercambiar lo que usas.

Si realmente desea poner objetos en ella que difieran solo en la forma en que hacen algo, el polimorfismo es una mejor forma de hacerlo. Usted puede tener una clase base que se derivan de:

std::map< std::string, boost::shared_ptr<Base> > table; 
table["hello"] = boost::shared_ptr<Base>(new Apple(...)); 
table["hello"]->print(); 

Lo cual básicamente requiere esta clase de diseño:

class Base { 
public: 
    virtual ~Base() { } 
    // derived classes implement this: 
    virtual void print() = 0; 
}; 

class Apple : public Base { 
public: 
    virtual void print() { 
     // print us out. 
    } 
}; 

El boost::shared_ptr es una llamada puntero inteligente. Eliminará tus objetos automáticamente si los quitas de tu mapa y ya nada más hace referencia a ellos. En teoría, podría haber trabajado con un puntero simple también, pero usar un puntero inteligente aumentará en gran medida la seguridad.Lee el manual shared_ptr al que he vinculado.

+0

Esa es la comprobación de tipo de tiempo de ejecución opción. Para una opción de verificación de tipo de tiempo de compilación, también existe la variante boost :: (uso con patrón de visitante para una solución estanca de verificación de tipo estática). –

+0

bueno, hablamos de eso en irc creo :) para todos los demás que lo lean: la variante solo puede almacenar un conjunto limitado de tipos, y si uno quiere obtener un valor de una entrada, uno puede usar visitas (y lo hará). obtener el valor sin saber su tipo), pero uno no puede guardar el valor en cualquier lugar. –

+0

porque la decisión de a qué versión del visitante se llama se realiza en tiempo de ejecución. el tipo de variable, sin embargo, se determina en tiempo de compilación. por lo que no puede hacer SomeAutoDeducedType result = boost :: apply_visitor (my_visitor(), u); . todos los op() de un visitante obtuvieron el mismo tipo de devolución. –

2

¿Se puede usar una unión con std :: map?

La variante Boost :: proporciona variables sin tipo.

Alternamente puede hacer que todos sus datos de Value sean privados y proporcionar accesores que devuelvan un error (o throw) si no está configurado.

+0

Puede usar una unión con std :: map, pero las uniones solo pueden contener objetos de tipo POD. – mstrobl

1

Una optimización simple sería usar un union, ya que siempre solo tendrá uno de los valores como clave.

Una solución más completa encapsularía información de tipo de tiempo de ejecución en una interfaz. Principalmente "¿Qué tipo es este?" y "¿Cómo comparo valores para la igualdad?" Luego usa implementaciones de eso como clave.

+0

Las uniones no pueden contener std :: string's – MSalters

+0

De hecho, g ++ se queja de "error: member 'std :: string [...]' con el constructor no permitido en unión" (entre otras cosas). Mi C++ se está oxidando: - / –

Cuestiones relacionadas