简体   繁体   中英

Shorthand for for-loop - syntactic sugar in C++(11)

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.

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
}

Use this:

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

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

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))

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

for(auto i : range(10, 15)) { cout << i << '\\n'; } 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'; } 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.

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

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".

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>::reference s 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.

对于第二个问题,如果您使用的是最新的Visual Studio版本,请键入“if”,然后键入TabTabTab以填写初始值,升级等。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM