C++11 和 C++17 基于范围的 For 循环是否可以迭代到特定位置而不是地图的整个范围?

Can C++11 and C++17 Range-Based For Loop iterate to a specific position instead of full range of the map?

是否有C++11和C++17版本的基于范围的For循环迭代器可以迭代到地图中的某个位置? 例如,如果一个映射有 10 个键值元素,那么我如何才能只遍历前三个键值元素? 以下代码仅遍历地图的整个范围。

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

您要么需要一个外部计数器来提前退出,例如:

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

或者,如果你不怕复制地图,你可以这样做:

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

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

最后,如果您可以使用C++20,那么您可以简单地这样做:

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

Range-based for 循环只是 syntactic-sugar 超过正常的 begin()end() 调用。由于 std::map 没有 random-access 迭代器这一事实使您的部分迭代请求变得复杂 - 这会导致每个迭代器递增的成本。

为了避免多余的成本,您最好的选择是我们 range-based for 循环到一个特定的计数器 break:

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

如果您正在寻找“标准”方法,可以使用 std::for_each_n 来选择长度。您只需要确保计数不超过容器的长度:

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

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

尽管 std::for_each_n 方法在很大程度上等同于使用计数器的第一种方法。


如果扩展支持, 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'
}

这种方法的缺点是 std::map 迭代器不是 random-access —— 这意味着您要付出迭代序列 两次 的代价。在大多数情况下,仅仅尝试利用 range-based for 循环并不值得。

编辑: 请参阅@InnocentBystander 的回答 std::views::take(...) 作为替代方案,这将有效地产生等同于 count+break 的内容基于方法。