C++(11)中的for循环速记 - 语法糖

21

实际上这是两个相关的问题。

我知道在C++11中有一个新的语法可以用于基于范围的for循环,它的形式为:

//v is some container
for (auto &i: v){
   // Do something with i
}

第一个问题:在这个循环中,我如何推断自己正在进行的迭代次数?(比如我想要在位置j填充值为j的向量)。

第二个问题:我想知道是否还有其他形式的循环写法。

for (int i=0; i<100; i++) { ... }

我觉得现在的写法有点繁琐,因为我经常这样做,所以我希望有一种更简洁的语法来代替它。 大致上应该是这样:

for(i in [0..99]){ ... }

希望你能做到会更好。

对于这两个问题,我希望避免使用额外的库。


2
如果你只想在位置j处用值j填充一个向量,你可以使用std::iota()函数。 - Anton Savin
4
请逐个提问,这样我们可以关闭每个重复问题并将其视为您没有搜索过的现有问题的副本。 :) - Lightness Races in Orbit
@Anton 谢谢你的 iot!虽然这不是我想要的全部,但是知道这个确实很好。 - dingalapadum
类似的内容在此处:https://dev59.com/JWYs5IYBdhLWcg3wFP94#46253024 - Manohar Reddy Poreddy
8个回答

21

第一种回答:你不需要改变。你已经使用了一个简单的结构来完成一个简单的任务;如果你有更复杂的需求,你需要使用更复杂的结构。

第二种回答:你可以创建一个生成连续整数值的迭代器类型和一个给出该范围的“容器”类型。除非你有充分的理由自己去做,否则Boost已经有这样的东西

#include <boost/range/irange.hpp>

for (int i : boost::irange(0,100)) {
    // i goes from 0 to 99 inclusive
}

从C++20开始,或者使用range-v3时,可以使用ranges::views::iota替换boost::irange - underscore_d

17

使用这个:

size_t pos = 0;
for (auto& i : v) {
    i = pos;
    ++pos;
}

(Boost很好,但并不被所有人普遍接受。)


9

对于第一个问题,答案非常简单:如果您需要迭代次数,请不要使用抽象了迭代次数的语法结构。只需使用普通的for循环而不是基于范围的循环。

对于第二个问题,我认为标准库中目前没有任何东西可用,但您可以使用boost::irange来实现:

for (int i : boost::irange(0, 100))

好的。谢谢。但我想避免为此使用额外的库。我会编辑我的问题。 - dingalapadum
5
如果你将Boost视为“额外库”,那么你就会在痛苦和重复造轮子方面浪费很多时间。最好把它当作“标准库的补充”来处理。 - Angew is no longer proud of SO
1
即使现在大多数系统都有boost,但随着依赖数量的增加,它会导致编译速度变慢,有时甚至会有点不稳定。因此,选择不使用它是一个值得尊重的选择。 - Aracthor
3
另一方面,Boost 是如此常见,以至于许多其他代码已知不会与之冲突。如果编写自己的类似代码,可能会发现意外的重载等问题。至于速度,所有主要的编译器制造商都将 Boost 作为其中一个基准测试。 - MSalters
2
他们最近隔离了最常用的功能来创建Boost.Core和一些更小的模块,以减少总体依赖。同时,为了加快编译速度,Boost.Intrusive和Boost.Containers在Boost 1.58中已经部分重写。 - Morwenn

4
对于第二个问题 - 如果Boost太重了,您可以使用这个库:

for(auto i : range(10, 15)) { cout << i << '\n'; }将打印10 11 12 13 14

for(auto i : range(20, 30, 2)) { cout << i << '\n'; }将打印20 22 24 26 28

双精度和其他数字类型也受支持。

它还具有其他Pythonic迭代工具,并且是头文件库。


2
你可以使用 Boost.Range 来实现这两种功能:http://boost.org/libs/range 为了简洁起见(并且为了增加一些趣味,因为 boost::irange 已经被单独演示过了),这里提供一个演示这些功能如何结合使用的示例代码:
// boost::adaptors::indexed
// http://www.boost.org/doc/libs/master/libs/range/doc/html/range/reference/adaptors/reference/indexed.html
#include <boost/range/adaptor/indexed.hpp>

// boost::irange
// http://www.boost.org/doc/libs/master/libs/range/doc/html/range/reference/ranges/irange.html
#include <boost/range/irange.hpp>

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> input{11, 22, 33, 44, 55};
    std::cout << "boost::adaptors::indexed" << '\n';
    for (const auto & element : input | boost::adaptors::indexed())
    {
        std::cout << "Value = " << element.value()
                  << " Index = " << element.index()
                  << '\n';
    }

    endl(std::cout);

    std::cout << "boost::irange" << '\n';
    for (const auto & element : boost::irange(0, 5) | boost::adaptors::indexed(100))
    {
        std::cout << "Value = " << element.value()
                  << " Index = " << element.index()
                  << '\n';
    }

    return 0;
}

示例输出:

boost::adaptors::indexed
Value = 11 Index = 0
Value = 22 Index = 1
Value = 33 Index = 2
Value = 44 Index = 3
Value = 55 Index = 4

boost::irange
Value = 0 Index = 100
Value = 1 Index = 101
Value = 2 Index = 102
Value = 3 Index = 103
Value = 4 Index = 104

1
如果 v 是一个向量(或任何一个std连续容器),那么
for(auto& x : v ) {
  size_t i = &x-v.data();
  x = i;
}

将第i项设置为值i

计数输出迭代器相对容易编写。Boost提供了一个名为irange的迭代器范围,可轻松生成此类迭代器。

提取容器的索引相对容易。我编写了一个名为indexes的函数,可以接受一个容器或整数范围,并在所涉及的范围内生成随机输出迭代器。

这样你就得到了:

for (size_t i : indexes(v) ) {
  v[i] = i;
}

在 Boost 库中可能有一个等效的容器索引范围函数。

如果你需要两者,而且不想自己动手,你可以编写一个“拉链”。

for( auto z : zip( v, indexes(v) ) ) {
  auto& x = std::get<0>(z);
  size_t i = std::get<1>(z);
  x = i;
}

zip 函数接收两个或更多可迭代范围(或容器),并生成一个元素为 iterator_traits<It>::reference 元组的范围视图。

这里是 Boost zip 迭代器:http://www.boost.org/doc/libs/1_41_0/libs/iterator/doc/zip_iterator.html -- 很可能有一个 Boost zip 范围可以处理类似上面 zip 函数的语法。


1
对于第二个问题:
还有另一种方法,但我不建议使用。然而,如果你想快速设置一个测试,你可以写下以下内容:
如果你不想使用库,并且只提供范围的上限,你可以写下:
for (auto i:vector<bool>(10)) {
    cout << "x";
}

这将创建一个大小为10的布尔向量,其中包含未初始化的值。使用i循环遍历这些未初始化的值(因此不要使用i),它将打印10次“x”。

有趣。在这种情况下,i会是什么?这会创建一个大小为10的新布尔向量吗?或者这里到底发生了什么? - dingalapadum
是的,它创建了一个大小为10的向量,绝对是过度设计,但是可能的。 ;) i 将是向量中未初始化的布尔值。 - Stuck
好的,这是一种比较昂贵的循环...我真的很想点赞,因为我觉得它很有创意...但另一方面,我永远不会使用或推荐给任何人...而且,它也不像是一种简写或语法糖。不过,如果你把你提到的那些细节放到帖子里,并澄清缺点,我愿意点赞作为努力的奖励。 - dingalapadum

-2
对于第二个问题,如果您正在使用最新的Visual Studio版本,请键入“if”,然后按TabTabTab来填写初始值、递增等。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接