2015-09-03 4 views
5

Допустим, у нас есть std::set<int>, и мы хотим создать std::vector<int> со всеми значениями из этого набора:Инициализировать контейнер с диапазоном итератора контейнера с различным типом

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

Это очень просто и элегантно. Но скажем, у меня есть std::map<std::string,int>, и я хочу скопировать все значения в std::vector<int>. К сожалению нет конструктора, который принимает диапазон итераторов и функцию преобразователя. Почему такого конструктора нет? Есть ли еще один простой и элегантный способ инициализации одного контейнера с разными значениями типа?

ответ

5

преобразующие Использование итераторы:

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

Кроме того, использование наддува :: адаптеры:

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

Или же, как и выше:

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

Обратите внимание, что использование диапазона конструктора вектора является более эффективным, чем решение с участием back_inserter.

+1

Лично я предпочитаю оператор «труба» при использовании повышающих адаптеров 'auto range = m | boost :: adapters :: values; ' – Alan

+0

@Alan Я предпочитаю оператор вызова функции для вызовов функций, и его короче ввести (' | 'vs'() '). –

0

Я думаю, что лучшее, что вы можете сделать, это использовать циклы на основе диапазона или функцию for_each.

1

std::map имеет другую модель памяти, сохраняя значения как std::pair. Распаковка - это не работа конструктора. Вы можете создать свой вектор, reserve, равный карте size и iterate над картами, чтобы подтолкнуть целые числа к обратной стороне вашего вектора. std::transform slims это.

+0

'std :: set' также имеет другую модель памяти, которая не мешает мне инициализировать вектор из его диапазона. – Slava

+0

Поскольку ваши итераторы набора возвращают только ожидаемый промежуток, карта возвращает пару, которая не может быть отправлена ​​на ожидаемый вывод. – Youka

2

С 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

Возможно, у downvoter (а не меня) еще не было времени, чтобы написать комментарий и все еще пишу его –

+0

Не я, но вызов через 'std :: mem_fn', скорее всего, не будет встроен. –

+1

@AaronMcDaid, который будет довольно долгим комментарием – Slava

Смежные вопросы