Iteração sobre elementos de par em um recipiente de pares (C ++)
-
10-07-2019 - |
Pergunta
Se eu tiver um recipiente (vector
, list
, etc) onde cada elemento é uma std::pair
, há uma maneira fácil de iterar sobre cada elemento de cada par?
i.
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));
e, em seguida, ser capaz de interagir sobre o valor:? 1,3,2,3,4,2,5,2,1,5
Da mesma forma, o tipo de functor / função voltaria a me um recipiente (do mesmo tipo) com um apartamento lista dos elementos par como acima?
Solução
Para aplainar seu recipiente de pares em um segundo recipiente que você também pode simplesmente escrever a sua própria inserção:
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));
Outras dicas
Para o seu primeiro, você tem que criar sua própria classe iterator, que pares de um sinalizador que indica a posição dentro de par com um container<pair>
iterador
Para o segundo, é mais fácil, apesar de ser tão geral como você quer (recipiente de mesmo tipo) você precisa de um modelo typedef . Aqui é para apenas vector:
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;
}
Veja como você fingir um typedef template:
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);
O código a seguir irá imprimir todos os valores conforme necessário:
for ( size_t x = 0; x < a.size(); ++x ) {
cout << a[x].first << "," << a[x].second << ",";
}
Eu prefiro esta maneira fácil de criar iterador personalizado.
Não há nenhuma maneira simples de realizar a iteração você quiser, mas você pode querer dar uma olhada na biblioteca boost :: iterator_adaptor ou implementar seu próprio iterador para fazê-lo (ele não deve ser muito complexo). Em seguida, na segunda pergunta, você pode usar std :: copy com o seu novo adaptador de iterador.
Não, não há realmente uma coisa dessas para std::pair
. Você pode querer considerar o uso de um Tuple impulso em seu lugar. Uma tupla é um pouco como uma versão expandida do std::pair
que permite que um número arbitrário de elementos (até certo limite, mas normalmente pelo menos 10), e dá acesso aos elementos algo como um vetor / array bem (ou seja, você pode acessar os elementos por nome ou índice).
TR1 também inclui std :: tr1 :: tuple, que é um subconjunto de tupla do Boost, mas se serve de memória, ele ainda inclui a funcionalidade nome / index que você está pedindo.
Edit: Note-se que em ambos os casos, a notação de índice requer um tempo de compilação constante para o índice, então você não pode escrever um (run-time) loop para iterar sobre os elementos em uma tupla - mas você pode fazer o trabalho com um pouco de metaprogramming. fusion impulso inclui um pouco para apoiar o que você precisa (por alguma estranha coincidência, tupla é parte da biblioteca de fusão).
Em algum momento você precisa usar início e segunda , mesmo se você criar sua própria classe iterator. Eu não acho que há um caminho para sair dela (pelo menos, de uma forma portátil).