62

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
  • 171,901
  • 28
  • 288
  • 402
user1101674
  • 1,341
  • 2
  • 12
  • 15
  • 1
    Why do you want to loop over a range of integers in the first place? – Xeo Feb 26 '13 at 12:27
  • 2
    @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
  • 3
    @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

39

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
  • 13,100
  • 5
  • 45
  • 79
  • 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
35

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,267
  • 1
  • 17
  • 18
BЈовић
  • 62,405
  • 41
  • 173
  • 273
  • 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
  • 6
    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
35

With C++20 we will have ranges. If you don't have access to C++20, 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.

If you do have access to C++20, this version works out of the box:

#include <ranges>
#include <iostream>

int main() {  
    for (int i : std::views::iota(1, 10)) {
        std::cout << i << ' ';
    }
}
Fureeish
  • 12,533
  • 4
  • 32
  • 62
  • 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
  • What's nice about this is that you can mark your iterator variable `i` `const`. – bers Aug 07 '23 at 13:32
3

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,248
  • 13
  • 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,731
  • 1
  • 30
  • 46
Emilio Garavaglia
  • 20,229
  • 2
  • 46
  • 63
  • 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
-3

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.