Вопрос

Если у меня есть контейнер (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).

В какой-то момент вам нужно будет использовать первый и второй, даже если вы создадите свой собственный класс итератора.Я не думаю, что есть выход из этой ситуации (по крайней мере, в портативном виде).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top