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.
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. –
Chris, gracias por señalar eso. Ahora lo estoy llamando una "matriz asociativa". –
relacionado: ["¿Cuál es el punto de las matrices heterogéneas?"] (Http://stackoverflow.com/questions/4534612/what-is-the-point-of-heterogenous-arrays) –