Contenedores heterogéneos en C ++

Vi este lindo gráfico que clasifica qué contenedor STL sería adecuado en función de los diferentes requisitos de datos, tales como:

– Tamaño fijo Vs Tamaño variable

– Datos del mismo tipo Vs diferentes tipos

– Ordenado Vs datos no ordenados

– Acceso aleatorio V secuencial

http://plasmahh.projectiwear.org/cce_clean.svg

Observo en esa imagen, que C ++ STL no hay contenedor que sea

  1. Tamaño variable
  2. Heterogéneo (datos de diferentes tipos).

¿C ++ no tiene algo para esto?

PD: Puede haber muchas permutaciones hechas de las diferentes propiedades de los contenedores y muchas otras también podrían no proporcionarse en STL.

En general, los Contenedores C ++ están diseñados para contener objetos de un solo tipo utilizando plantillas. Si desea diferentes tipos que se derivan de un tipo, puede almacenar un contenedor de punteros (supongo que también podría tener un contenedor de vacío * a cualquier cosa …), por ejemplo, std :: vector .

Si desea tipos completamente independientes, puede almacenar objetos que puedan hacer referencia de forma segura a esos otros tipos, como boost :: any.

http://www.boost.org/doc/libs/1_47_0/doc/html/any.html

Algunos ejemplos del sitio de impulso:

 #include  #include  using boost::any_cast; typedef std::list many; void append_int(many & values, int value) { boost::any to_append = value; values.push_back(to_append); } void append_string(many & values, const std::string & value) { values.push_back(value); } bool is_int(const boost::any & operand) { return operand.type() == typeid(int); } bool is_char_ptr(const boost::any & operand) { try { any_cast(operand); return true; } catch(const boost::bad_any_cast &) { return false; } } 

boost :: variant es similar, pero usted especifica todos los tipos permitidos, en lugar de permitir cualquier tipo en su contenedor.

http://www.boost.org/doc/libs/1_47_0/doc/html/variant.html

 std::vector< boost::variant > vec; vec.push_back( 44); vec.push_back( "str" ); vec.push_back( SomthingElse(55, 65) ); //not allowed 

El principio básico en la biblioteca estándar es que los “contenedores” son homogéneos; el estándar de C ++ no considera que cosas como std::pair o std::tuple sean contenedores. (Considero que el gráfico es engañoso, ya que los considera contenedores). Si necesita un contenedor heterogéneo, debería usar un contenedor de boost::variant , o algo similar.

std::pair y std::tuple son apenas contenedores C ++ … así que no, no hay contenedores heterogéneos en el STL, porque no es necesario tenerlos incorporados.

Hay varios enfoques para crear tales contenedores. Los enfoques que recomendaría son:

  • usando polymorphism
  • usando un tipo de variante

Para el polymorphism, puede consultar la biblioteca Boost Pointer Container .

 boost::ptr_vector vec; vec.push_back(new Derived); vec.push_back(new Derived2); 

Imita los contenedores STL, pero proporciona funcionalidades orientadas hacia el polymorphism:

  • Elementos de acceso como Base&
  • Manejo automático de la memoria
  • Comportamiento de copia específico (utilizando métodos new_clone )
  • Azúcar sintáctico: boost::ptr_vector::iterator it; dado boost::ptr_vector::iterator it; , *it es una Base&

Si sus tipos no están relacionados, la otra posibilidad es usar el Boost Variant . Básicamente, una variante es similar a:

 enum { Type1, Type2, ... } _type; union { SomeType1 _1; SomeType2 _2; ... } _u; 

Por supuesto, dado que es un impulso, proporciona garantías específicas para garantizar que solo pueda acceder al miembro de la unión que está actualmente activo y elimina la restricción de clases con constructores / destructores que no pueden utilizarse en las uniones tradicionales.

También proporciona instalaciones, como el static_visitor , que es el equivalente a un interruptor en el tipo, y hará que el error de comstackción salga si no se visita uno de los estados posibles.

Los contenedores heterogéneos de tamaño fijo (como std::tuple requieren que los tipos se conozcan en tiempo de comstackción. Si desea crear un contenedor heterogéneo de tamaño variable, simplemente std::vector> un std::vector> .

Si desea un contenedor heterogéneo donde los tipos no se conocen en tiempo de comstackción (ya sea que sea de tamaño variable o fijo) tendrá que almacenar punteros (o punteros inteligentes) a un tipo base conocido en tiempo de comstackción o, alternativamente, considerar algo como un contenedor de boost::any . El STL no proporciona directamente dicho contenedor en tamaño fijo o variable con elementos heterogéneos determinados en tiempo de ejecución.

Una biblioteca que todavía no se acepta en Boost. Pero lo que se propuso para la inclusión está dirigido a esto:

http://rawgit.com/joaquintides/poly_collection/website/doc/html/index.html

Proporciona una buena clase llamada any_collection que permite tener un contenedor heterogéneo a través de boost :: type_erasure :: any: http://rawgit.com/joaquintides/poly_collection/website/doc/html/poly_collection/tutorial.html#poly_collection. tutorial.basics.boost_any_collection

De lo contrario, en C ++ 17 hay una forma sencilla de implementar esto: https://gieseanw.wordpress.com/2017/05/03/a-true-heterogeneous-container-in-c/

Citando el ejemplo del artículo antes mencionado:

 namespace andyg{ struct heterogeneous_container{ private: template static std::unordered_map> items; public: template  void push_back(const T& _t) { items[this].push_back(_t); } }; // storage for our static members template std::unordered_map> heterogeneous_container::items; } // andyg namespace 

Luego utilizable fácilmente:

 andyg::heterogeneous_container c; c.push_back(1); c.push_back(2.f); c.push_back('c'); struct LocalStruct{}; c.push_back(LocalStruct{}); 

El autor afirma que es una implementación de juguete, pero creo que esta es una forma realmente inteligente de implementarlo, y tiene una ventaja de simplicidad sobre poly_collection o un vector de variantes.

Si el elemento que almacena sería un boost::any o boost::variant entonces puede almacenar indirectamente datos heterogéneos.