Como usar o loop for () baseado em intervalo com std :: map?

336

O exemplo comum para loops for () baseados no intervalo do C ++ 11 é sempre algo simples como este:

std::vector<int> numbers = { 1, 2, 3, 4, 5, 6, 7 };
for ( auto xyz : numbers )
{
     std::cout << xyz << std::endl;
}

Nesse caso, xyzé um int. Mas, o que acontece quando temos algo como um mapa? Qual é o tipo da variável neste exemplo:

std::map< foo, bar > testing = { /*...blah...*/ };
for ( auto abc : testing )
{
    std::cout << abc << std::endl;         // ? should this give a foo? a bar?
    std::cout << abc->first << std::endl;  // ? or is abc an iterator?
}

Quando o contêiner que está sendo atravessado é algo simples, parece que os loops baseados em intervalo para () nos fornecerão cada item, não um iterador. O que é legal ... se fosse o iterador, a primeira coisa que sempre precisaríamos fazer é desreferenciar isso de qualquer maneira.

Mas estou confuso sobre o que esperar quando se trata de mapas e mapas múltiplos.

(Ainda estou no g ++ 4.4, enquanto os loops baseados em intervalo estão no g ++ 4.6+, então ainda não tive a chance de experimentá-lo.)

Stéphane
fonte
4
O intervalo para declaração faz uma dança profana com a biblioteca std::begine std::endfunções padrão ou funções-membro com o mesmo nome.
Gene Bushuyev 7/08
10
@ willill Em um exemplo de três linhas, você está sendo pego pelo nome falso da variável?
Stéphane

Respostas:

495

Cada elemento do contêiner é um map<K, V>::value_type, que é um typedefpara std::pair<const K, V>. Conseqüentemente, no C ++ 17 ou superior, você pode escrever

for (auto& [key, value]: myMap) {
    std::cout << key << " has value " << value << std::endl;
}

ou como

for (const auto& [key, value]: myMap) {
    std::cout << key << " has value " << value << std::endl;
}

se você não planeja modificar os valores.

No C ++ 11 e C ++ 14, você pode usar forloops aprimorados para extrair cada par por conta própria e extrair manualmente as chaves e os valores:

for (const auto& kv : myMap) {
    std::cout << kv.first << " has value " << kv.second << std::endl;
}

Você também pode marcar a kvvariável constse desejar uma visualização somente leitura dos valores.

templatetypedef
fonte
95

No C ++ 17, isso é chamado de ligações estruturadas , o que permite o seguinte:

std::map< foo, bar > testing = { /*...blah...*/ };
for ( const auto& [ k, v ] : testing )
{
  std::cout << k << "=" << v << "\n";
}
Dalle
fonte
É possível obter um const &para a chave, mas uma referência não-constante ao valor? (porque isso é o mapa :: value_type faz ...)
Peterchen
2
@peterchen: ké constse você usarfor(auto&[k,v]:testing)
dalle
11
cpppreference em ligações estruturadas pt.cppreference.com/w/cpp/language/structured_binding
TankorSmash
Se você está compilando com GCC você precisará da versão 7 ou melhor para ligações estruturadas: gcc.gnu.org/projects/cxx-status.html
csknk
25

Deste artigo: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2049.pdf

for( type-specifier-seq simple-declarator : expression ) statement

é sintaticamente equivalente a

{
    typedef decltype(expression) C;
    auto&& rng(expression);
    for (auto begin(std::For<C>::begin(rng)), end(std::For<C>::end(rng)); begin != end; ++ begin) {
        type-specier-seq simple-declarator(*begin);
        statement
    }
}

Assim, você pode ver claramente que será o abcseu caso std::pair<key_type, value_type >. Portanto, para impressão, você pode acessar cada elemento por abc.firsteabc.second

AK
fonte
3

Se o operador de atribuição de cópias de foo e bar for barato (por exemplo, int, char, ponteiro etc.), você pode fazer o seguinte:

foo f; bar b;
BOOST_FOREACH(boost::tie(f,b),testing)
{
  cout << "Foo is " << f << " Bar is " << b;
}
balki
fonte
4
O primeiro trecho de código não está usando um "C ++ 11 baseado em intervalo para ()". Não é uma resposta para "C ++ 11: como usar o loop for () baseado em intervalo com std :: map?"
Isoiphone
11
@ytj Já foi mencionado na resposta que não funciona. Não quero remover isso para que novos usuários não precisem tentar e descobrir o fato novamente.
balki