Home > Software design >  Can C 11 and C 17 Range-Based For Loop iterate to a specific position instead of full range of the
Can C 11 and C 17 Range-Based For Loop iterate to a specific position instead of full range of the

Time:03-04

Are there versions of C 11 and C 17 Range-Based For Loop iterators that can iterate to a certain position in map? For example, if a map has 10 key-value elements, then how can I iterate through only the first three key-value elements? The following codes only iterate through the full range of the map.

//C  11    
for(auto m: mapData){
cout << m.first << ": " << m.second << endl;
}
//C  17
for(auto [key, val]: mapData){
    cout << key << ": " << val << endl;
}

CodePudding user response:

You either need an external counter to make early exit, eg:

int n = 0;
for(auto [k, v] : map)
{
    if(  n > 10) break;
    std::cout << k << ": " << v << std::endl;
}

Or, if you are not afraid of copying the map, you can do:

std::map<...> copy { map.begin(), std::next(map.begin(), 10) };

for(auto [k, v] : copy) std::cout << k << ": " << v << std::endl;

Finally, if you can use C 20, then you can simply do this:

#include <ranges>
for(auto [k, v] : map | std::views::take(10))
{
    std::cout << k << ": " << v << std::endl;
}

CodePudding user response:

Range-based for loops are just syntactic-sugar over normal begin() and end() calls. Your request of a partial iteration are complicated by the fact that std::map does not have random-access iterators -- which incurs a cost per iterator incrementing.

To avoid redundant costs, your best option would be to us range-based for loops to a specific counter with a break:

auto count = 0;
for (const auto& [k,v] : map) {
  if (  count > n) { break; }
  // process 'k', 'v'
}

If you are looking for a "standard" approach, you can use std::for_each_n which lets you choose the length. You just need to ensure that the count doesn't exceed the length of container:

auto length = std::min(map.size(), n);

std::for_each_n(map.begin(), n, [&](const auto& kv) {
    // process kv
});

Though the std::for_each_n approach is largely equivalent to the first approach with a counter.


If you expand to support , your options open up a bit since you can construct a std::ranges::subrange:

for (const auto& [k,v] : std::ranges::subrange(map.begin(), std::advance(map.end(), n)) {
  // process 'k', 'v'
}

The downside with this approach is that std::map iterators are not random-access -- which means you are paying the cost of iterating the sequence twice. For the most part, this isn't really worth it just to try to leverage range-based for loops.

Edit: See @InnocentBystander's answer with std::views::take(...) as an alternative, which will effectively produce something equivalent to the count break-based approach.

  • Related