contenitori eterogenei in C ++
-
25-10-2019 - |
Domanda
ho visto questo grafico bello che classifica quale contenitore STL sarebbe adatto in base alle diverse esigenze di dati come:
- Dimensione fissa Vs dimensione variabile
- I dati dello stesso Tyme Vs tipo diverso
- Ordinati Vs dati non ordinati
- sequenziale Vs accesso casuale
http://plasmahh.projectiwear.org/cce_clean.svg
Ho notato in quella immagine, che il C ++ STL non v'è alcun contenitore che è
- Formato variabile
- eterogenea (dati di tipo diverso).
Non C ++ avere qualcosa per questo?
PS -. Ci possono essere molti permutazioni fatte le diverse proprietà dei contenitori e molti altri ancora potrebbero non essere forniti in AWL
Soluzione
Bene generalmente C ++ I contenitori sono stati progettati per gli oggetti in possesso di un solo tipo utilizzando i modelli. Se si desidera che i diversi tipi che sono tutti derivati ??da un tipo è possibile memorizzare un contenitore di puntatori (immagino si potrebbe anche avere un contenitore di * vuoto a qualsiasi cosa ...) ad esempio, std :: vector
Se si desidera tipi completamente indipendenti, è possibile memorizzare gli oggetti che possono fare riferimento in modo sicuro questi altri tipi, come boost :: qualsiasi.
http://www.boost.org/doc/ libs / 1_47_0 / doc / html / any.html
Alcuni esempi dal sito spinta:
#include <list>
#include <boost/any.hpp>
using boost::any_cast;
typedef std::list<boost::any> 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<const char *>(operand);
return true;
}
catch(const boost::bad_any_cast &)
{
return false;
}
}
boost :: variante è simile, ma è necessario specificare tutti i tipi permessi, piuttosto che permettere qualsiasi tipo nel contenitore.
http://www.boost.org/doc/ libs / 1_47_0 / doc / html / variant.html
std::vector< boost::variant<unsigned, std::string> > vec;
vec.push_back( 44);
vec.push_back( "str" );
vec.push_back( SomthingElse(55, 65) ); //not allowed
Altri suggerimenti
Il principio di base nella libreria standard è che "contenitori" sono omogenei; lo standard C ++ non prende in considerazione le cose come std::pair
o std::tuple
per essere dei contenitori. (Mi piacerebbe prendere in considerazione il grafico fuorviante, dal momento che li considera come contenitori.) Se avete bisogno di un contenitore eterogeneo, che avrebbe dovuto usare un contenitore di boost::variant
, o qualcosa del genere.
std::pair
e std::tuple
sono difficilmente C ++ contenitori .... quindi no, non ci sono contenitori eterogenei nel STL, perché non è necessario averli built-in.
Ci sono diversi approcci per la creazione di tali contenitori. Gli approcci mi sento di raccomandare sono:
- utilizzando il polimorfismo
- utilizzando un tipo variante
Per polimorfismo, è possibile controllare Boost Pointer Container biblioteca.
boost::ptr_vector<Base> vec;
vec.push_back(new Derived);
vec.push_back(new Derived2);
imita i contenitori STL, ma fornisce funzionalità orientate verso polimorfismo:
- elementi di Access come
Base&
- gestione della memoria automatica
- comportamento specifico di copia (utilizzando metodi
new_clone
) - zucchero sintattico: dato
boost::ptr_vector<Base>::iterator it;
,*it
è unBase&
Se i tipi sono estranei, l'altra possibilità è quella di utilizzare Boost Variante . Fondamentalmente, una variante è simile a:
enum { Type1, Type2, ... } _type;
union {
SomeType1 _1;
SomeType2 _2;
...
} _u;
Naturalmente, dal momento che di spinta, fornisce garanzie specifiche per assicurarsi che sia possibile accedere solo il membro del sindacato che è attualmente attivo e solleva la restrizione classi con costruttori / distruttori non essere utilizzabile in unioni tradizionali.
Fornisce inoltre servizi, come il static_visitor
, che è l'equivalente di un interruttore del tipo, e farà l'errore di compilazione se uno dei possibili stati non viene visitato.
Una libreria che non è ancora accettato nel Boost. Ma che è stato proposto per l'inclusione si rivolge verso questo:
http://rawgit.com/joaquintides/poly_collection/website /doc/html/index.html
Si fornisce una classe bel nome any_collection che permette di avere un contenitore eterogeneo con boost :: :: type_erasure qualsiasi: http: // rawgit. com / joaquintides / poly_collection / sito web / doc / html / poly_collection / tutorial.html # poly_collection.tutorial.basics.boost_any_collection
In caso contrario, in C ++ 17 ci sono modo semplice per implementare questa: https://gieseanw.wordpress.com / 2017/05/03 / un-vero-eterogenea-contenitore-in-c /
Citando l'esempio suddetto articolo:
namespace andyg{
struct heterogeneous_container{
private:
template<class T>
static std::unordered_map<const heterogeneous_container*, std::vector<T>> items;
public:
template <class T>
void push_back(const T& _t)
{
items<T>[this].push_back(_t);
}
};
// storage for our static members
template<class T>
std::unordered_map<const heterogeneous_container*, std::vector<T>> heterogeneous_container::items;
} // andyg namespace
Poi utilizzabile facilmente:
andyg::heterogeneous_container c;
c.push_back(1);
c.push_back(2.f);
c.push_back('c');
struct LocalStruct{};
c.push_back(LocalStruct{});
L'autore afferma che è un'implementazione giocattolo, ma penso che questo è un modo molto intelligente per la sua attuazione, e ha un vantaggio semplicità sopra poly_collection o un vettore di varianti.
I contenitori eterogenei dimensione fissa (come std::tuple
richiedono i tipi di essere conosciuti al momento della compilazione. Se si vuole fare una variabile di dimensioni contenitore eterogeneo, basta fare un std::vector<std::tuple<T1,T2,...,TN>>
.
Se si desidera un contenitore eterogeneo dove i tipi non è noto al momento della compilazione (se che sarebbe variabile o fisso dimensioni) dovrete puntatori negozio (o puntatori intelligenti) per un tipo di base noto in fase di compilazione, o prendere in considerazione in alternativa, qualcosa di simile a un contenitore di boost::any
. STL non fornisce direttamente un tale contenitore sia fisso o variabile dimensioni con elementi eterogenei determinate in fase di esecuzione.
Se l'elemento si memorizza sarebbe un boost::any
o boost::variant
allora si può indirettamente memorizzare i dati eterogenei.