จะใช้ range-based สำหรับ () loop กับ std :: map ได้อย่างไร?


336

ตัวอย่างทั่วไปสำหรับลูป C ++ 11 ที่ใช้สำหรับ () เป็นบางสิ่งที่ง่ายเช่นนี้เสมอ:

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

ซึ่งในกรณีที่เป็นxyz intแต่จะเกิดอะไรขึ้นเมื่อเรามีบางอย่างที่เหมือนแผนที่ ชนิดของตัวแปรในตัวอย่างนี้คืออะไร:

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

เมื่อคอนเทนเนอร์ที่ถูกสำรวจเป็นสิ่งที่เรียบง่ายดูเหมือนว่าช่วงตาม () สำหรับลูปจะให้แต่ละรายการไม่ใช่ตัววนซ้ำ อันไหนดี ... ถ้าเป็นตัววนซ้ำสิ่งแรกที่เราต้องทำเสมอคือการอ้างถึงมันอยู่ดี

แต่ฉันสับสนว่าจะคาดหวังอะไรเมื่อพูดถึงสิ่งต่างๆเช่นแผนที่และ Multimaps

(ฉันยังอยู่ใน g ++ 4.4 ในขณะที่ลูปอิงช่วงอยู่ใน g ++ 4.6+ ดังนั้นฉันยังไม่ได้ลองเลย)


4
ช่วงสำหรับคำสั่งเป็นการเต้นรำที่ไม่บริสุทธิ์ด้วยไลบรารีstd::beginและstd::endฟังก์ชันมาตรฐานหรือฟังก์ชันสมาชิกภายใต้ชื่อเดียวกัน
Gene Bushuyev

10
@ จะเป็นตัวอย่าง 3 บรรทัดคุณจะได้รับชื่อตัวแปรปลอมหรือไม่
Stéphane

คำตอบ:


495

องค์ประกอบของภาชนะแต่ละคนเป็นmap<K, V>::value_typeซึ่งเป็นสำหรับtypedef std::pair<const K, V>ดังนั้นใน C ++ 17 หรือสูงกว่าคุณสามารถเขียน

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

หรือเป็น

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

หากคุณไม่ได้วางแผนที่จะแก้ไขค่า

ใน C ++ 11 และ C ++ 14 คุณสามารถใช้forลูปที่ปรับปรุงแล้วเพื่อแยกแต่ละคู่ด้วยตนเองจากนั้นแยกคีย์และค่าด้วยตนเอง:

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

คุณสามารถพิจารณาการทำเครื่องหมายkvตัวแปรconstถ้าคุณต้องการมุมมองแบบอ่านอย่างเดียวของค่า


95

ใน C ++ 17 สิ่งนี้เรียกว่าการโยงแบบมีโครงสร้างซึ่งอนุญาตให้ทำสิ่งต่อไปนี้:

std::map< foo, bar > testing = { /*...blah...*/ };
for ( const auto& [ k, v ] : testing )
{
  std::cout << k << "=" << v << "\n";
}

เป็นไปได้หรือไม่ที่จะรับ a const &key แต่เป็นการอ้างอิงที่ไม่ใช่ค่าอ้างอิง? (เพราะนั่นคือแผนที่ :: value_type ทำ ... )
peterchen

2
@peterchen: kคือconstถ้าคุณใช้for(auto&[k,v]:testing)
Dalle

1
cpppreference ในโครงสร้างการเชื่อมโยงen.cppreference.com/w/cpp/language/structured_binding
TankorSmash

หากคุณกำลังรวบรวมกับ GCC คุณต้องมีเวอร์ชัน 7 หรือดีกว่าสำหรับการโยงแบบมีโครงสร้าง: gcc.gnu.org/projects/cxx-status.html
csknk

25

จากบทความนี้: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2049.pdf

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

เทียบเท่ากับวากยสัมพันธ์

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

ดังนั้นคุณสามารถเห็นได้ชัดเจนว่าสิ่งที่เป็นในกรณีของคุณจะabc std::pair<key_type, value_type >ดังนั้นสำหรับการพิมพ์คุณสามารถเข้าถึงแต่ละองค์ประกอบด้วยabc.firstและabc.second


15

หากคุณต้องการเห็นคีย์ / ค่าจากแผนที่ของคุณและชอบการใช้บูสต์คุณสามารถใช้อะแดปเตอร์เพิ่มพร้อมลูปตามช่วง:

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

มีการเพิ่มการเทียบเท่า :: อะแดปเตอร์ :: key_values

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


3

หากผู้ประกอบการมอบหมายให้ทำสำเนาของ foo และ bar นั้นราคาถูก (เช่น int, char, pointer เป็นต้น) คุณสามารถทำสิ่งต่อไปนี้:

foo f; bar b;
BOOST_FOREACH(boost::tie(f,b),testing)
{
  cout << "Foo is " << f << " Bar is " << b;
}

4
ตัวอย่างโค้ดแรกไม่ได้ใช้ "ช่วง C ++ 11 สำหรับ ()" มันไม่ได้คำตอบสำหรับ "C ++ 11: วิธีการใช้ช่วงสำหรับ () วนรอบกับ std :: map?"
isoiphone

1
@ytj มีการกล่าวถึงแล้วในคำตอบที่ไม่ได้ผล ฉันไม่ต้องการลบเพื่อให้ผู้ใช้ใหม่ไม่ต้องลองและค้นหาความจริงอีกครั้ง
balki
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.