2015-09-03 9 views
5

Powiedzmy mamy std::set<int> i chcemy stworzyć std::vector<int> ze wszystkimi wartościami z tego zestawu:Inicjalizacja pojemnik z zakresu iteratora pojemnika z innego typu

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

Jest to prosty i elegancki. Ale załóżmy, że mam std::map<std::string,int> i chcę skopiować wszystkie wartości do std::vector<int>. Niestety nie ma konstruktora, który akceptowałby zakres iteratorów i funkcję konwertera. Dlaczego nie ma takiego konstruktora? Czy istnieje inny prosty i elegancki sposób na zainicjowanie jednego kontenera o różnych wartościach?

Odpowiedz

5

Zastosowanie przekształcenia iteratory:

#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)); 
} 

Alternatywnie, przypominającego użycie :: adaptery:

#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()); 
} 

lub takie same jak powyżej:

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

Należy pamiętać, że stosując zakres konstruktora wektor jest bardziej wydajny niż rozwiązanie z udziałem back_inserter.

+1

Osobiście wolę operatora "potoku", gdy używam adapterów doładowania "auto range = m | boost :: adaptery :: values; ' – Alan

+0

@Alan Preferuję operator wywołania funkcji dla wywołań funkcji, a jest on krótszy do typu (' | 'kontra'() '). –

0

Myślę, że najlepiej można zrobić, używając cykli opartych na odległościach lub funkcji for_each.

1

std::map ma inny model pamięci, przechowujący wartości jako std::pair. Rozpakowanie ich nie jest zadaniem konstruktora. Możesz stworzyć swój wektor, pamięć reserve równą mapie size i iterate przez pary map, aby wypchnąć twoje liczby całkowite z tyłu twojego wektora. std::transform wyszczupla to.

+0

'std :: set' również ma inny model pamięci, który nie powstrzymuje mnie od inicjalizacji wektora z jego zakresu. – Slava

+0

Ponieważ twoje zestawy iteratorów zwracają tylko oczekiwanego intergera, mapa zwraca parę, która nie może być rzutowana na oczekiwane wyjście. – Youka

2

Z 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

Być może downvoter (nie ja) nie miał jeszcze czasu na napisanie komentarza i nadal go pisze –

+0

Nie ja, ale wywoływanie przez 'std :: mem_fn' prawdopodobnie nie zostanie zainicjowane. –

+1

@AaronMcDaid to będzie cholernie długi komentarz – Slava

Powiązane problemy