2015-09-03 11 views
5

Diciamo che abbiamo std::set<int> e vogliamo creare un std::vector<int> con tutti i valori di quel set:inizializzare un contenitore con la gamma iteratore di contenitore con diverso tipo

std::set<int> set; 
std::vector<int> vec(set.begin(), set.end()); 

Questo è semplice ed elegante. Ma diciamo che ho un std::map<std::string,int> e voglio copiare tutti i valori su std::vector<int>. Sfortunatamente non esiste un costruttore che accetta range di iteratori e funzioni di conversione. Perché non è previsto un tale costruttore? C'è un altro modo semplice ed elegante per inizializzare un contenitore con valori di tipo diversi?

risposta

5

Usa trasformare iteratori:

#include <boost/iterator/transform_iterator.hpp> 
#include <vector> 
#include <map> 

int main() { 
    std::map<int, double> m; 
    auto f = [](auto&& pair) { return pair.second; }; 
    std::vector<double>(boost::make_transform_iterator(m.begin(), f), 
         boost::make_transform_iterator(m.end(), f)); 
} 

In alternativa, l'uso boost :: adattatori:

#include <boost/range/adaptor/map.hpp> 
#include <vector> 
#include <map> 

int main() { 
    std::map<int, double> m; 
    auto range = boost::adaptors::values(m); 
    std::vector<double>(range.begin(), range.end()); 
} 

O la stessa di cui sopra:

auto v = boost::copy_range<std::vector<double> >(boost::adaptors::values(m)); 

Si noti che usando la gamma del costruttore del vettore è più efficiente di una soluzione che comprenda back_inserter.

+1

Personalmente, preferisco l'operatore "pipe" quando uso gli adattatori boost 'auto range = m | boost :: adapters :: values; ' – Alan

+0

@Alan Preferisco l'operatore di chiamata di funzione per le chiamate di funzione, ed è più breve da digitare (' | 'vs'() '). –

0

Penso che il meglio che si possa fare sia utilizzare i cicli basati sull'intervallo o la funzione for_each.

1

std::map ha un altro modello di memoria, che memorizza i valori come std::pair. Disimballare questi non è il lavoro di un costruttore. È possibile creare il vettore,memoria uguale alla mappa size e iterate su coppie di mappe per spingere i numeri interi sul retro del vettore. std::transform slims quello.

+0

'std :: set' ha anche un altro modello di memoria, che non mi impedisce di inizializzare un vettore da un intervallo di esso. – Slava

+0

Poiché gli iteratori dell'insieme restituiscono solo l'interger previsto, una mappa restituisce una coppia che non può essere convertita nell'output previsto. – Youka

2

Con boost::transform_iterator:

#include <functional> 
#include <boost/iterator/transform_iterator.hpp> 

std::map<std::string, int> m{ {"a", 1}, {"b", 2} }; 
auto second = std::mem_fn(&std::map<std::string, int>::value_type::second); 

std::vector<int> vec(boost::make_transform_iterator(std::begin(m), second) 
        , boost::make_transform_iterator(std::end(m), second)); 

DEMO

+0

Forse il downvoter (non me) non ha ancora avuto il tempo di scrivere un commento e lo sta ancora scrivendo –

+0

Non io, ma il richiamo di 'std :: mem_fn' probabilmente non sarà in linea. –

+1

@AaronMcDaid che sarà un commento dannatamente lungo – Slava

Problemi correlati