Перебор парных элементов в контейнере пар (C++)
-
10-07-2019 - |
Вопрос
Если у меня есть контейнер (vector
, list
, и т. д.), где каждый элемент представляет собой std::pair
, есть ли простой способ перебрать каждый элемент каждой пары?
то есть
std::vector<std::pair<int,int> > a;
a.push_back(std::pair(1,3));
a.push_back(std::pair(2,3));
a.push_back(std::pair(4,2));
a.push_back(std::pair(5,2));
a.push_back(std::pair(1,5));
а затем иметь возможность перебирать значение:1,3,2,3,4,2,5,2,1,5?
Аналогично, какой тип функтора/функции вернет мне контейнер (того же типа) с плоским списком парных элементов, как указано выше?
Решение
Чтобы объединить контейнер пар во второй контейнер, вы также можете просто написать свой собственный инструмент вставки:
template<class C>
struct Inserter {
std::back_insert_iterator<C> in;
Inserter(C& c) : in(c) {}
void operator()(const std::pair<typename C::value_type, typename C::value_type>& p)
{
*in++ = p.first;
*in++ = p.second;
}
};
template<class C>
Inserter<C> make_inserter(C& c)
{
return Inserter<C>(c);
}
// usage example:
std::list<int> l;
std::for_each(a.begin(), a.end(), make_inserter(l));
Другие советы
Для начала вам нужно создать свой собственный класс итератора, который объединяет флаг, указывающий позицию внутри пары, с container<pair>
итератор
Во втором случае это проще, хотя, чтобы быть настолько общим, насколько вы хотите (контейнер того же типа), вам нужен определение типа шаблона.Вот просто вектор:
template <class V>
std::vector<V> flatten_pairs(std::vector<std::pair<V,V> > const& a) {
typedef std::vector<std::pair<V,V> > A;
std::vector<V> ret;
for (typename A::const_iterator i=a.begin(),e=a.end();i!=e;++i) {
ret.push_back(i->first);
ret.push_back(i->second);
}
return ret;
}
Вот как можно подделать typedef шаблона:
template <class C>
struct same_container;
template <class V>
struct same_container<std::vector<V> > {
template <class W> struct rebind { typedef std::vector<W> type; };
};
template <class V>
struct same_list<std::list<V> > {
template <class W> struct rebind { typedef std::list<W> type; };
};
template <class C>
typename same_container<C>::rebind<typename C::value_type::first_type>::type
flatten_pairs(C const& a);
Следующий код выведет все необходимые значения:
for ( size_t x = 0; x < a.size(); ++x ) {
cout << a[x].first << "," << a[x].second << ",";
}
Я бы предпочел этот простой способ, чем создание собственного итератора.
Не существует простого способа выполнить нужную вам итерацию, но вы можете взглянуть на библиотеку boost::iterator_adaptor или реализовать для этого свой собственный итератор (он не должен быть слишком сложным).Затем, что касается второго вопроса, вы можете использовать std::copy с новым адаптером итератора.
Нет, на самом деле ничего подобного не существует. std::pair
.Возможно, вместо этого вы захотите использовать Boost Tuple.Кортеж немного похож на расширенную версию std::pair
это допускает произвольное количество элементов (до некоторого предела, но обычно не менее 10) и также дает доступ к элементам, что-то вроде вектора/массива (т.е.вы можете получить доступ к элементам по имени или индексу).
TR1 также включает в себя std::tr1::tuple, который является подмножеством кортежа Boost, но если память не изменяет, он по-прежнему включает в себя функции имени/индекса, которые вы запрашиваете.
Редактировать:обратите внимание, что в обоих случаях индексная запись требует время компиляции константа для индекса, поэтому вы не можете написать цикл (время выполнения) для перебора элементов в кортеже, но вы можете выполнить эту работу, применив немного метапрограммирования.Boost fusion включает в себя довольно многое для поддержки того, что вам нужно (по какому-то странному совпадению, кортеж является частью библиотеки fusion).
В какой-то момент вам нужно будет использовать первый и второй, даже если вы создадите свой собственный класс итератора.Я не думаю, что есть выход из этой ситуации (по крайней мере, в портативном виде).