21

Actually these are two related questions.

I know there is a new syntax in C++11 for range-based for loops of the form:

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

First question: how can I infer at which iteration I am in this loop? (Say I want to fill a vector with value j at position j).

Second question: I wanted to know if there also is some other way to write a loop of the form

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

I find this way of writing it a bit cumbersome, and I do this so often and I would love to have a more concise syntax for it. Something along the lines:

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

would be great.

For both questions I would like to avoid having to use additional libraries.

dingalapadum
  • 2,077
  • 2
  • 21
  • 31

8 Answers8

21

First answer: you don't. You've used a simple construct for a simple purpose; you'll need something more complicated if you have more complicated needs.

Second answer: You could make an iterator type that yields consecutive integer values, and a "container" type that gives a range of those. Unless you have a good reason to do it yourself, Boost has such a thing:

#include <boost/range/irange.hpp>

for (int i : boost::irange(0,100)) {
    // i goes from 0 to 99 inclusive
}
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
17

Use this:

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

(Boost is good, but it is not universally accepted.)

Peter K
  • 1,787
  • 13
  • 15
9

For the first question, the answer is pretty simple: if you need the iteration count, don't use the syntactic construct which abstracts away the iteration count. Just use a normal for loop and not the range-based one.

For the second question, I don't think there's anything currently in the standard library, but you could use a boost::irange for it:

for (int i : boost::irange(0, 100))
Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • ok. thx. but I would like to avoid having to use additional libs just for this. I'll edit my question. – dingalapadum Apr 08 '15 at 11:14
  • 5
    @dingalapadum You'll save yourself a world of pain and wheel reinvention if you don't consider Boost an "additional lib." Just treat it as a "standard library addendum." – Angew is no longer proud of SO Apr 08 '15 at 11:15
  • 1
    @Angew Even if today boost is present on most systems, it makes compilation slower, and sometimes a little bit unstable, as dependence number grows up. So it's a respectable choice to not want to use it. – Aracthor Apr 08 '15 at 11:24
  • 3
    @Aracthor: On the other hand, Boost is so common that a lot of other code is known not to conflict with it. Write your own equivalent, and you may find unintended overloads etc. As for speed, all major compilers makers use Boost as one of the benchmarks. – MSalters Apr 08 '15 at 11:52
  • 2
    And they recently isolated the most used features to create Boost.Core and some more little modules in order to reduce the overall dependency. And Boost.Intrusive and Boost.Containers have been partly rewritten for Boost 1.58 so that they compile faster. – Morwenn Apr 08 '15 at 12:07
4

For the second question - if Boost is too heavy, you could always use this library:

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

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

Doubles and other numeric types are supported too.

It has other pythonic iteration tools and is header-only.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
onqtam
  • 4,356
  • 2
  • 28
  • 50
2

You can do both of these things with Boost.Range: http://boost.org/libs/range

For brevity (and to spice things up a little, since boost::irange has been already demonstrated in isolation), here's a sample code demonstrating these features working together:

// 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;
}

Sample output:

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
Matt
  • 629
  • 9
  • 21
1

If v is a vector (or any std contiguous container), then

for(auto& x : v ) {
  size_t i = &x-v.data();
  x = i;
}

will set the ith entry to the value i.

An output iterator that counts is reasonably easy to write. Boost has one and has an easy-to-generate range of them called irange.

Extracting the indexes of a container is relatively easy. I have written a function called indexes that can take a container, or a range of integers, and produces random output iterators over the range in question.

That gives you:

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

There probably is an equivalent container-to-index range function in Boost.

If you need both, and you don't want to do the work, you can write a zipper.

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

where zip takes two or more iterable ranges (or containers) and produces a range view over tuples of iterator_traits<It>::references to the elements.

Here is Boost zip iterator: http://www.boost.org/doc/libs/1_41_0/libs/iterator/doc/zip_iterator.html -- odds are there is a Boost zip range that handles syntax like the above zip function.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
1

For the 2nd question:

There is another way, but I would not use or recommend it. However, for quickly setting up a test you could write:

if you do not want to use a library and you are fine with only providing the top bound of the range you can write:

for (auto i:vector<bool>(10)) {
    cout << "x";
}

This will create a boolean vector of size 10, with uninitialized values. Looping through these unitialized values using i (so do not use i) it will print 10 times "x".

Stuck
  • 11,225
  • 11
  • 59
  • 104
  • Interesting. what will `i` be in this case? Does this create a new vector of bools of size 10? Or what happens here exactly? – dingalapadum Feb 14 '17 at 16:54
  • Yes, it creates a vector of size 10, is absolutly overkilling the purpose, but possible. ;) `i` will be the uninitialized boolean values of the vector. – Stuck Feb 14 '17 at 20:27
  • Ok. kind of an expensive loop... I'd really like to upvote because I find it creative... then again, I would never use this or recommend this to anybody... also, it's not like a shorthand or syntactic sugar either. Still, if you put those details you mentioned into the post and clarify the drawbacks I'm willing to upvote as a reward for the effort. – dingalapadum Feb 14 '17 at 21:32
-2

For the second question, if you are using the latest Visual Studio versions, type 'if' then Tab, Tab, and Tab to fill in init value, step-up and so on.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Sheen
  • 3,333
  • 5
  • 26
  • 46