Wie verwende ich eine bereichsbasierte for () - Schleife mit std :: map?

336

Das gängige Beispiel für bereichsbasierte for () - Schleifen in C ++ 11 ist immer so einfach:

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

In welchem ​​Fall xyzist ein int. Aber was passiert, wenn wir so etwas wie eine Karte haben? Was ist der Typ der Variablen in diesem Beispiel:

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?
}

Wenn der zu durchlaufende Container etwas Einfaches ist, sieht es so aus, als würden bereichsbasierte for () -Schleifen uns jedes Element geben, keinen Iterator. Was schön ist ... wenn es ein Iterator wäre, müssten wir es immer als erstes dereferenzieren.

Aber ich bin verwirrt, was mich erwartet, wenn es um Dinge wie Karten und Multimaps geht.

(Ich bin immer noch auf g ++ 4.4, während bereichsbasierte Schleifen in g ++ 4.6+ sind, also hatte ich noch keine Gelegenheit, es zu versuchen.)

Stéphane
quelle
4
Der Bereich für Anweisungen führt einen unheiligen Tanz mit der Standardbibliothek std::beginund std::endFunktionen oder Mitgliedsfunktionen unter demselben Namen durch.
Gene Bushuyev
10
@will In einem dreizeiligen Beispiel werden Sie vom falschen Variablennamen erfasst?
Stéphane

Antworten:

495

Jedes Element des Containers ist ein map<K, V>::value_type, was ein typedeffür ist std::pair<const K, V>. Folglich können Sie in C ++ 17 oder höher schreiben

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

oder als

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

Wenn Sie nicht vorhaben, die Werte zu ändern.

In C ++ 11 und C ++ 14 können Sie erweiterte forSchleifen verwenden, um jedes Paar einzeln zu extrahieren und anschließend die Schlüssel und Werte manuell zu extrahieren:

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

Sie können die kvVariable auch markieren , constwenn Sie eine schreibgeschützte Ansicht der Werte wünschen.

templatetypedef
quelle
95

In C ++ 17 wird dies als strukturierte Bindung bezeichnet , was Folgendes ermöglicht:

std::map< foo, bar > testing = { /*...blah...*/ };
for ( const auto& [ k, v ] : testing )
{
  std::cout << k << "=" << v << "\n";
}
dalle
quelle
Ist es möglich, einen const &zum Schlüssel zu bekommen , aber einen nicht konstanten Verweis auf den Wert? (weil das ist, was map :: value_type macht ...)
Peterchen
2
@ Peterchen: kist, constwenn Sie verwendenfor(auto&[k,v]:testing)
dalle
1
cpppreference auf strukturierten Bindungen en.cppreference.com/w/cpp/language/structured_binding
TankorSmash
Wenn Sie mit GCC kompilieren, benötigen Sie Version 7 oder besser für strukturierte Bindungen: gcc.gnu.org/projects/cxx-status.html
csknk
25

Aus diesem Artikel: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2049.pdf

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

ist syntaktisch äquivalent zu

{
    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
    }
}

So können Sie deutlich sehen, was abcin Ihrem Fall sein wird std::pair<key_type, value_type >. So zum Drucken Sie können jedes Element Zugriff tun durch abc.firstundabc.second

AK
quelle
15

Wenn Sie nur die Schlüssel / Werte auf Ihrer Karte sehen möchten und Boost verwenden möchten, können Sie die Boost-Adapter mit den bereichsbasierten Schleifen verwenden:

for (const auto& value : myMap | boost::adaptors::map_values)
{
    std::cout << value << std::endl;
}

Es gibt einen äquivalenten boost :: adapters :: key_values

http://www.boost.org/doc/libs/1_51_0/libs/range/doc/html/range/reference/adaptors/reference/map_values.html

Pixie-Poop
quelle
3

Wenn der Kopierzuweisungsoperator von foo und bar billig ist (z. B. int, char, pointer usw.), können Sie Folgendes tun:

foo f; bar b;
BOOST_FOREACH(boost::tie(f,b),testing)
{
  cout << "Foo is " << f << " Bar is " << b;
}
balki
quelle
4
Der erste Codeausschnitt verwendet kein "C ++ 11-Bereich-basiertes für ()". Es ist keine Antwort auf "C ++ 11: Wie verwende ich eine bereichsbasierte for () - Schleife mit std :: map?"
Isoiphone
1
@ytj In der Antwort wird bereits erwähnt, dass es nicht funktioniert. Ich möchte das nicht entfernen, damit neue Benutzer es nicht versuchen und die Tatsache erneut herausfinden müssen.
Balki