Bagaimana cara menggunakan rentang berbasis untuk () loop dengan std :: map?

336

Contoh umum untuk rentang C ++ 11 berbasis untuk () selalu sesuatu yang sederhana seperti ini:

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

Dalam hal xyzini adalah int. Tapi, apa yang terjadi ketika kita memiliki sesuatu seperti peta? Apa jenis variabel dalam contoh ini:

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

Ketika kontainer yang dilalui adalah sesuatu yang sederhana, sepertinya rentang berbasis untuk () loop akan memberi kita setiap item, bukan iterator. Yang bagus ... jika itu iterator, hal pertama yang harus selalu kita lakukan adalah tetap melakukannya.

Tapi saya bingung apa yang diharapkan ketika datang ke hal-hal seperti peta dan multimaps.

(Saya masih menggunakan g ++ 4.4, sedangkan loop berbasis rentang ada di g ++ 4.6+, jadi saya belum memiliki kesempatan untuk mencobanya.)

Stéphane
sumber
4
Rentang pernyataan membuat tarian tidak suci dengan perpustakaan standar std::begindan std::endfungsi atau fungsi anggota dengan nama yang sama.
Gene Bushuyev
10
@ akankah pada contoh 3-line, Anda terjebak pada nama variabel palsu?
Stéphane

Jawaban:

495

Setiap elemen wadah adalah a map<K, V>::value_type, yang adalah typedefuntuk std::pair<const K, V>. Akibatnya, dalam C ++ 17 atau lebih tinggi, Anda dapat menulis

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

atau sebagai

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

jika Anda tidak berencana mengubah nilai.

Dalam C ++ 11 dan C ++ 14, Anda dapat menggunakan forloop yang disempurnakan untuk mengekstrak masing-masing pasangan sendiri, lalu mengekstrak kunci dan nilai secara manual:

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

Anda juga dapat mempertimbangkan menandai kvvariabel constjika Anda ingin tampilan hanya baca dari nilai-nilai.

templatetypedef
sumber
96

Dalam C ++ 17 ini disebut binding terstruktur , yang memungkinkan untuk hal berikut:

std::map< foo, bar > testing = { /*...blah...*/ };
for ( const auto& [ k, v ] : testing )
{
  std::cout << k << "=" << v << "\n";
}
dalle
sumber
Apakah mungkin untuk mendapatkan const &kunci, tetapi referensi non-const ke nilai? (karena itulah yang dilakukan map :: value_type ...)
peterchen
2
@ Peterchen: kadalah constjika Anda menggunakanfor(auto&[k,v]:testing)
dalle
1
cppreferensi pada binding terstruktur en.cppreference.com/w/cpp/language/structured_binding
TankorSmash
Jika Anda mengompilasi dengan GCC, Anda memerlukan versi 7 atau lebih baik untuk binding terstruktur: gcc.gnu.org/projects/cxx-status.html
csknk
25

Dari makalah ini: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2049.pdf

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

secara sintaksis setara dengan

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

Jadi Anda dapat dengan jelas melihat apa yang ada abcdalam kasus Anda nantinya std::pair<key_type, value_type >. Jadi untuk mencetak Anda dapat mengakses setiap elemen dengan abc.firstdanabc.second

AK
sumber
3

Jika operator penyalinan foo and bar murah (mis. Int, char, pointer dll), Anda dapat melakukan hal berikut:

foo f; bar b;
BOOST_FOREACH(boost::tie(f,b),testing)
{
  cout << "Foo is " << f << " Bar is " << b;
}
balki
sumber
4
Cuplikan kode pertama tidak menggunakan "C ++ 11 range-based for ()". Ini bukan jawaban untuk "C ++ 11: bagaimana cara menggunakan rentang berbasis untuk () loop dengan std :: map?"
isoiphone
1
@ytj Sudah disebutkan dalam jawaban bahwa itu tidak berfungsi. Saya tidak ingin menghapusnya sehingga pengguna baru tidak perlu mencobanya dan mencari tahu faktanya lagi.
balki