Як використовувати діапазон для () циклу з 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?
}

Коли контейнер, який пересувається, є чимось простим, схоже, що на основі діапазону для () циклів буде подано нам кожен елемент, а не ітератор. Що приємно ... якби це був ітератор, перше, що ми завжди мали би зробити - це знеструмити його все одно.

Але я розгублений, чого очікувати, якщо мова йде про такі речі, як карти та мультимапи.

(Я все ще перебуваю на g ++ 4.4, тоді як цикли на основі діапазону знаходяться в g ++ 4.6+, тому я ще не мав можливості спробувати це.)


4
Діапазон для операції нечемно танцює зі стандартною бібліотекою std::beginта std::endфункціями або членами під одним іменем.
Гена Бушуєва

10
@will На 3-рядковому прикладі ви потрапляєте у підроблену назву змінної?
Stéphane

Відповіді:


495

Кожен елемент контейнера є a map<K, V>::value_type, що є typedefдля std::pair<const K, V>. Отже, в С ++ 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";
}

Чи можна отримати const &ключ до ключа, але не-const посилання на значення? (адже це те, що робить map :: 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;
}

є еквівалентний boost :: адаптери :: 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?"
ізоіфон

1
@ytj У відповіді вже згадується, що це не працює. Я не хочу цього видаляти, щоб новим користувачам не потрібно було це пробувати і знову з'ясовувати факт.
balki
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.