Как скопировать значения из std::map в std::vector с помощью std::copy
Цель – в одну строку скопировать все значения из std::map в std::vector.
Да, вместо использования std::copy можно просто в цикле перебрать все значения словаря и закинуть их в вектор. Тем не менее, интересует именно реализация с помощью std::copy.
Минимальный пример:
#include <iostream>
#include <string>
#include <map>
#include <algorithm>
int main()
{
std::map<int, std::string> m;
m[0] = "Hello";
m[7] = "Heey";
m[-59] = "R";
m[1024] = "Rrrrrrrrroooooooooocccccccckkkkkk";
std::vector<std::string> res;
// Хочется, чтобы строка работала как-то так, но проблема в том, что элементы словаря – пары, а не только значения
// std::copy(m.begin(), m.end(), res.begin());
// Здесь планирую получить вывод в виде всех значений словаря m
for (std::vector<std::string>::const_iterator elem = res.begin(); elem != res.end(); ++elem)
std::cout << *elem << std::endl;
return 0;
}
Таким образом, с моей точки зрения, вопрос сводится к следующему: как получить итератор на последовательность значений словаря, не беря во внимание ключи? То есть, чтобы, разыменовав итератор, получать не std::pair, а std::string в моём случае.
P.S. Использую C++07, то есть, мне не доступен функционал, появившийся в C++11 (Просьба не спрашивать, почему. Так нужно и нужно не мне.)
Ответы (2 шт):
К сожалению std::copy для этой цели не годится. Рекомендую воспользоваться алгоритмом std::for_each или std::transform. Для старого стандарта придется написать функтор, а для С++11 и выше достаточно будет лямбды:
Пример решения с for_each:
#include <iostream> #include <string> #include <map> #include <algorithm> //Функтор для вставки в вектор (в С++11 в нем нет необходимости) class BackInserter { public: explicit BackInserter(std::vector<std::string>& vec) : m_ref_vec(vec) {} void operator()(const std::pair<int, std::string>& element) { m_ref_vec.push_back(element.second); } private: std::vector<std::string>& m_ref_vec; }; int main() { std::map<int, std::string> m; m[0] = "Hello"; m[7] = "Heey"; m[-59] = "R"; m[1024] = "Rrrrrrrrroooooooooocccccccckkkkkk"; std::vector<std::string> res; BackInserter fInserter(res); std::for_each(m.begin(), m.end(), fInserter); // C++11 //std::for_each( // m.begin(), // m.end(), // [&res](const std::pair<int, std::string>& element) //{ // res.push_back(element.second); //}); // Здесь планирую получить вывод в виде всех значений словаря m for (std::vector<std::string>::const_iterator elem = res.begin(); elem != res.end(); ++elem) std::cout << *elem << std::endl; return 0; }Пример решения с transform:
#include <iostream> #include <string> #include <map> #include <algorithm> class TransformFunctor { public: std::string operator()(const std::pair<int, std::string>& element) { return element.second; } }; int main() { std::map<int, std::string> m; m[0] = "Hello"; m[7] = "Heey"; m[-59] = "R"; m[1024] = "Rrrrrrrrroooooooooocccccccckkkkkk"; std::vector<std::string> res; res.resize(m.size()); std::transform(m.begin(), m.end(), res.begin(), TransformFunctor()); //C++11 //std::transform( // m.begin(), // m.end(), // res.begin(), // [](const std::pair<int, std::string>& element)->std::string //{ // return element.second; //}); // Здесь планирую получить вывод в виде всех значений словаря m for (std::vector<std::string>::const_iterator elem = res.begin(); elem != res.end(); ++elem) std::cout << *elem << std::endl; return 0; }
В С++ итератора, для значений словаря из коробки нет до сих пор (ещё не смотрел, поправят ли что-то в C++20). Печально, но факт. Зато своя обёртка для этого делается на коленке довольно просто. В элементарном случае это будет выглядеть как-то так:
template <typename T>
class second_unwrap_iterator : public T {
public:
second_unwrap_iterator(T base)
: T(base)
{ }
const typename T::value_type::second_type& operator*() {
return T::operator*().second;
}
};
template <typename T>
second_unwrap_iterator<T> second_unwraper(T base) {
return second_unwrap_iterator<T>(base);
}
// ...
std::copy(second_unwraper(m.cbegin()), second_unwraper(m.cend()),
std::back_inserter(res));
second_unwraper() необходим только для того чтобы не указывать параметры шаблона в конструкторе. Начиная с С++17 вывод типов в шаблонах улучшили и можно обойтись без оного:
std::copy(second_unwrap_iterator(m.begin()), second_unwrap_iterator(m.end()),
std::back_inserter(res));