Làm cách nào để sử dụng vòng lặp dựa trên phạm vi cho () với std :: map?


336

Ví dụ phổ biến cho các vòng lặp dựa trên phạm vi C ++ 11 luôn là một cái gì đó đơn giản như thế này:

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

Trong trường hợp đó xyzlà một int. Nhưng, điều gì xảy ra khi chúng ta có một cái gì đó giống như bản đồ? Loại biến trong ví dụ này là gì:

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

Khi container được duyệt là một cái gì đó đơn giản, có vẻ như các vòng lặp dựa trên phạm vi () sẽ cung cấp cho chúng ta từng mục, không phải là một trình vòng lặp. Thật tuyệt ... nếu đó là iterator, điều đầu tiên chúng ta luôn phải làm là loại bỏ nó.

Nhưng tôi bối rối không biết nên mong đợi điều gì khi nói đến những thứ như bản đồ và đa khung.

(Tôi vẫn đang sử dụng g ++ 4.4, trong khi các vòng dựa trên phạm vi nằm trong g ++ 4.6+, vì vậy tôi chưa có cơ hội dùng thử.)


4
Phạm vi cho câu lệnh thực hiện một điệu nhảy vô hồn với thư viện chuẩn std::beginvà các std::endhàm hoặc hàm thành viên dưới cùng tên.
Gene Bushuyev

10
@will Trên một ví dụ 3 dòng, bạn có bị cuốn vào tên biến giả không?
Stéphane

Câu trả lời:


495

Mỗi phần tử của container là một map<K, V>::value_type, đó là một typedefcho std::pair<const K, V>. Do đó, trong C ++ 17 trở lên, bạn có thể viết

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

hoặc như

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

nếu bạn không có kế hoạch sửa đổi các giá trị.

Trong C ++ 11 và C ++ 14, bạn có thể sử dụng forcác vòng lặp nâng cao để tự mình trích xuất từng cặp, sau đó trích xuất thủ công các khóa và giá trị:

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

Bạn cũng có thể xem xét đánh dấu kvbiến constnếu bạn muốn chế độ xem chỉ đọc các giá trị.


95

Trong C ++ 17, điều này được gọi là các ràng buộc có cấu trúc , cho phép thực hiện như sau:

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

Có thể lấy const &mã khóa, nhưng tham chiếu không phải là giá trị? (bởi vì đó là những gì bản đồ :: value_type làm ...)
peterchen

2
@peterchen: kconstnếu bạn sử dụngfor(auto&[k,v]:testing)
dalle

1
cpppreference về các ràng buộc có cấu trúc en.cppreference.com/w/cpp/lingu/structured_binding
TankorSmash 14/12/17

Nếu bạn đang biên dịch với GCC, bạn cần phiên bản 7 hoặc tốt hơn cho các ràng buộc có cấu trúc: gcc.gnu.org/projects/cxx-status.html
csknk

25

Từ bài viết này: http://www.open-std.org/jtc1/sc22/wg21/docs/ con / 2006 / n2049.pdf

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

về mặt cú pháp tương đương với

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

Vì vậy, bạn có thể thấy rõ những gì abctrong trường hợp của bạn sẽ là std::pair<key_type, value_type >. Vì vậy, để in bạn có thể truy cập từng phần tử bằng abc.firstabc.second


15

Nếu bạn chỉ muốn xem các khóa / giá trị từ bản đồ của mình và thích sử dụng boost, bạn có thể sử dụng các bộ điều hợp tăng cường với các vòng dựa trên phạm vi:

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

có một mức tăng tương đương :: bộ điều hợp :: key_values

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


3

Nếu toán tử gán gán của foo và bar rẻ (ví dụ: int, char, con trỏ, v.v.), bạn có thể làm như sau:

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

4
Đoạn mã đầu tiên không sử dụng "C ++ 11 dựa trên phạm vi cho ()". Đây không phải là câu trả lời cho "C ++ 11: cách sử dụng vòng lặp for () dựa trên phạm vi với std :: map?"
isoiphone

1
@ytj Nó đã được đề cập trong câu trả lời rằng nó không hoạt động. Tôi không muốn xóa nó để người dùng mới không phải thử nó và tìm hiểu lại thực tế.
balki
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.