49

Since C++11 introduced the range-based for loop (range-based for in c++11), what is the neatest way to express looping over a range of integers?

Instead of

for (int i=0; i<n; ++i)

I'd like to write something like

for (int i : range(0,n))

Does the new standard support anything of that kind?

Update: this article describes how to implement a range generator in C++11: Generator in C++

NathanOliver
  • 161,918
  • 27
  • 262
  • 366
user1101674
  • 1,161
  • 2
  • 11
  • 14
  • 1
    Why do you want to loop over a range of integers in the first place? – Xeo Feb 26 '13 at 12:27
  • @Xeo, are you serious? Looping over a range of integers is (I'm pretty certain) in _every_ piece of C source that isn't a simple `HelloWorld` program. Or did you mean: why would you want to use the range-based `for` for that? – paxdiablo Feb 26 '13 at 12:30
  • @paxdiablo: I'm dead-serious. Give me a good reason to loop over a range of integers. Do you use those integers to index something? Loop / iterate over that something instead. Explicit loops are highly error-prone. The only time I effectively use them is for benchmark loops. – Xeo Feb 26 '13 at 12:31
  • 1
    Boost.Range has irange. – Cat Plus Plus Feb 26 '13 at 12:34
  • 2
    @paxdiablo: C source is irrelevant. The question clearly explicitly pertains to C++11. – Puppy Feb 26 '13 at 12:35
  • 3
    @Xeo, I get you now. You have a good point except for one small use case (one I've discovered in many for/foreach questions). Sometimes you _need_ to know the index. Granted that's probably a small use case but I can envisage a couple of things that might need it - just the other day, I had my boy doing a 12-times table program albeit not in C++ since I believe that would be cruel punishment for a 9yo :-) – paxdiablo Feb 26 '13 at 12:37
  • 2
    @Xeo: There have been many times when I've needed to loop over a range of integers, if for no other reason than to loop over a container *and* have access to the index at the same time. – Nicol Bolas Feb 26 '13 at 12:39
  • @paxdiablo: That's when you zip range you're iterating on with an infinite integer range. Painful in C++ but well it's C++. – Cat Plus Plus Feb 26 '13 at 12:40
  • @Nicol: See the above two comments. – Xeo Feb 26 '13 at 12:45
  • 1
    @paxdiablo Yeah exactly, *C* code. Just that this question isn't about *C*, which doesn't have a range based for, anyway. Though I agree that there are indeed use-cases for iterating over a range of integers, just not that many as in *C* (and it hurts everytime I need to use an index loop instead of an iterator loop ;), but that may be just be). – Christian Rau Feb 26 '13 at 13:40

6 Answers6

38

While its not provided by C++11, you can write your own view or use the one from boost:

#include <boost/range/irange.hpp>
#include <iostream>

int main(int argc, char **argv)
{
    for (auto i : boost::irange(1, 10))
        std::cout << i << "\n";
}

Moreover, Boost.Range contains a few more interesting ranges which you could find pretty useful combined with the new for loop. For example, you can get a reversed view.

ixSci
  • 12,431
  • 5
  • 40
  • 71
  • 1
    Basically, one can implement a class which fullfills requirements of range based for on their own. – Euri Pinhollow Apr 05 '17 at 14:49
  • 1
    @EuriPinhollow, I've chosen bad wording. Updated now. – ixSci Apr 05 '17 at 15:00
  • Do you happen to know of a minimal C++14/C++17 self-contained implementation of `irange`? – einpoklum Jun 09 '18 at 20:11
  • 1
    @ixSci: Come on, using heap? This would be [terribly difficult for compilers](https://godbolt.org/g/vcPKXc). – einpoklum Jun 10 '18 at 07:49
  • @einpoklum, you asked for a small self-contained implementation. Do not like mine? Come up with your own. – ixSci Jun 10 '18 at 09:05
  • 1
    @ixSci: Ok, fair enough, but my point is that an implementation needs to (easily) translate into the same instructions as the plain for loop, and creating an std::vector, on the heap, is not that. – einpoklum Jun 10 '18 at 09:19
  • @einpoklum, you can always create a wrapper class which will conform to for loop interface which will be eliminated by a compiler on compile time and which will be just a simple number generator. But it will require more code and a little bit of thinking. That will also be not that small probably. Different needs — different implementations. If you just need a small implementation which is used in non-hot paths where you do not care about a malloc or two then my 5-min-to-write implementation might be appropriate. Otherwise we have to wait for co-routines for small **and** efficient impl. – ixSci Jun 10 '18 at 09:29
  • Alternatively, with range-v3 library you can do `for (auto i : ranges::view::iota(1, 10))`. – Daniel Langr Jun 13 '18 at 20:11
33

The neatest way is still this:

for (int i=0; i<n; ++i)

I guess you can do this, but I wouldn't call it so neat:

#include <iostream>

int main()
{
  for ( auto i : { 1,2,3,4,5 } )
  {
    std::cout<<i<<std::endl;
  }
}
Ezra Steinmetz
  • 1,207
  • 17
  • 18
BЈовић
  • 59,719
  • 40
  • 167
  • 261
  • 10
    I'm going to give +1 just for that first sentence. Sometimes the old ways are still the best :-) – paxdiablo Feb 26 '13 at 12:25
  • Unless n is an expensive expression of course, then irange is concise and performant. – Macke Dec 21 '16 at 11:54
  • 5
    This is not neat. It has three (or two if you want to split hairs) different imperative statements, which you have to put together to realize it's a loop. Not neat at all. Plus, it doesn't indicate whether `i` is significant or whether the author just wanted to run a piece of code `n` times. – einpoklum Jan 05 '17 at 18:10
  • @einpoklum: "neatest" != "neat, according to some implicit gold standard". All it meant was "no better alternative in pure, bare-foot C++ yet, _unfortunately_". – Sz. Jul 13 '19 at 09:00
  • 1
    I've typed `for (int j=0; j – Don Hatch Mar 03 '20 at 00:39
  • I was looking for a "neater" solution from another point of view: Due to `i`'s incrementation, `i` cannot be `const` although one might want to express that `i` shouldn't be modified (besides being incremented). The second solution, while not being "so neat", at least allows for making `i` `const`. – Roland Sarrazin Jul 21 '20 at 10:15
20

With C++20 we will have ranges. You can try them by downloading the lastest stable release from it's author, Eric Niebler, from his github, or go to Wandbox. What you are interested in is ranges::views::iota, which makes this code legal:

#include <range/v3/all.hpp>
#include <iostream>

int main() {
    using namespace ranges;

    for (int i : views::iota(1, 10)) {
        std::cout << i << ' ';
    }
}

What's great about this approach is that views are lazy. That means even though views::iota represents a range from 1 to 10 exclusive, no more than one int from that range exists at one point. The elements are generated on demand.

Barry
  • 267,863
  • 28
  • 545
  • 906
Fureeish
  • 11,475
  • 4
  • 28
  • 55
  • 1
    Nitpick: ranges-v3 is not usable with C++11 AFAICT, only C++14; and this question is about C++11 - it's tagged as such and OP said as much in the body. – einpoklum Aug 29 '19 at 22:18
  • @einpoklum I believe they can be used with C++11. Didn't test it though - I believe Eric Niebler, during one of his talk, stated that they are compatible with C++11. – Fureeish Aug 30 '19 at 06:45
  • The library documentation [says C++14 and up](https://ericniebler.github.io/range-v3/). Also - if it's not too much trouble, would you mind telling me at exactly which time you answered this question (it's related to some argument on meta)? I'm only seeing the hour-resolution time. – einpoklum Aug 30 '19 at 06:49
  • @einpoklum you are right regarding the documantation. I must have either remembered incorrectly or it must have been changed after Eric's talk in 2015 (highly likely). Regarding the exact timestamp - I do not remember that and I am not aware of SO tool that would allow me to check that. As an author of the answer - can I do that? – Fureeish Aug 30 '19 at 07:02
  • Perhaps you should include less than all of range/v3 ? – einpoklum Feb 22 '21 at 16:00
2

If you don't mind doing the loop in reverse order, you can replace

for (int i=0; i<n; ++i)

with simpler

for (int i=n; i--;)

Mike Redrobe
  • 1,150
  • 12
  • 12
1

Depending on what you have to do with the integer, consider the also the <numeric> header, in particular std::iota in conjunction with std::transform and std::fill depending on the cases.

Dev Null
  • 4,418
  • 1
  • 24
  • 41
Emilio Garavaglia
  • 19,487
  • 2
  • 43
  • 61
  • How can I trust the compiler to optimize all of that away and make it a simple loop again? Especially since you might need an auxiliary vector for the 0...n-1 values. – einpoklum Jan 05 '17 at 18:11
  • 5
    If you don't trust compiler optimization, use C not C++. – Emilio Garavaglia Jan 05 '17 at 20:09
  • 1
    I'm not talking about _that_ kind of compiler optimization. I'm talking those that are more out-there, like removing malloc and free calls when the compiler can tell they're not actually necessary, which is what you would need here. – einpoklum Jan 05 '17 at 20:11
-2

Well, I really like the solution, provided here (I'm sorry, it's not translated in English):

#define FOR(I,UPPERBND) for(int I = 0; I<int(UPPERBND); ++I)

The main idea is described in this way: when we talk about simple iteration-indexed-cycles, we do not need to think about it. However, when we use for(;;) construction- there are always three steps: initialization, end condition check, iteration. And this is an overkill for such a simple loop, as just i:[0,n). I liked the idea, that we want to write simple things in a simple way. When you see that FOR(i,N) - you just know, that there are nothing special. When you see the for(;;) construction - you have to be more careful and see all three parts of it. Just an example from that article:

for (int iter=0; iter<nb_iter; iter++) {          // some iterative computation
    for (int c=0; c<mesh.cells.nb(); c++)         // loop through all tetrahedra
        for (int lv0=0; lv0<4; lv0++)             // for every pair of
            for (int lv1 = lv0+1; lv1<4; lv1++)   // vertices in the tet
                for (int d=0; d<3; d++) {         // do stuff for each of 3 dimensions
                    nlRowScaling(weight);
                    nlBegin(NL_ROW);
                    nlCoefficient(mesh.cells.vertex(c, lv0)*3 + d,  1);
                    nlCoefficient(mesh.cells.vertex(c, lv1)*3 + d, -1);
                    nlEnd(NL_ROW);
                }
    [...]
}

become a:

FOR(iter, nb_iter) {
    FOR(c, mesh.cells.nb())
        FOR(lv0, 4)
            for (int lv1 = lv0+1; lv1<4; lv1++)
                FOR(d, 3) {
                    nlRowScaling(weight);
                    nlBegin(NL_ROW);
                    nlCoefficient(mesh.cells.vertex(c, lv0)*3 + d,  1);
                    nlCoefficient(mesh.cells.vertex(c, lv1)*3 + d, -1);
                    nlEnd(NL_ROW);
                }
    [...]
}

And you see, where you should concentrate your attention.