Farzam
Farzam

Reputation: 1360

range-based for in c++11

in c++ 11 if we have a set<int> S; we could say:

for (auto i: S)
    cout << i << endl;

but can we force i to be a iterator, I mean write a code that is equivalent to:

for (auto i = S.begin(); i != S.end(); i++)
    cout << (i != s.begin()) ? " " : "" << *i;

or could we do something that we can understand the index of i in the set(or vector)?

and another question is how could we say that don't do this for all elements in S but for first half of them or all of them except the first one.

or when we have a vector<int> V, and want to print its first n values what should we do? I know we can create a new vector but it takes time to copy a vector to a new vector.

Upvotes: 10

Views: 22973

Answers (6)

Dietmar K&#252;hl
Dietmar K&#252;hl

Reputation: 154035

Range-based for is intended for simple cases. I'd expect to to mildly useful while protoyping something but would expect uses of it mostly gone long before things actually become a product. It may possibly useful to make life for beginners easier, but this is an area I can't judge (but what seems to drive a lot of the recent C++ discussions).

The only somewhat constructive approach could be to use an adapter which references the underlying range and whose begin() and end() methods adjust the iterator appropriately. Also note that you probably want to hoist any special handling of the first or last element out of the loop processing the bulk of the data. Sure, it is only another check followed by a correctly predicted branch vs. no check and less pollution of the branch prediction tables.

Upvotes: 1

Damon
Damon

Reputation: 70206

No, unluckily. See what the standard says:

The range-based for statement for ( for-range-declaration : expression ) statement is equivalent to

{
    auto && __range = ( expression );
    for ( auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin ) {
        for-range-declaration = *__begin;
        statement
    }
}

where __range, __begin, and __end are variables defined for exposition only

In other words, it already iterates from begin to end and already dereferences the iterator, which you never get to see.

Upvotes: 20

Matthieu M.
Matthieu M.

Reputation: 300349

The principle of the range-based for is to iterate over the whole range.

However you decide what the range is, therefore you can operate on the range itself.

template <typename It>
class RangeView {
public:
  typedef It iterator;

  RangeView(): _begin(), _end() {}
  RangeView(iterator begin, iterator end): _begin(begin), _end(end) {}

  iterator begin() const { return _begin; }
  iterator end() const { return _end; }

private:
  iterator _begin;
  iterator _end;
};

template <typename C>
RangeView<typename C::iterator> rangeView(C& c, size_t begin, size_t end) {
  return RangeView<typename C::iterator>(
           std::next(c.begin(), begin),
           std::next(c.begin(), end)
         );
}

template <typename C>
RangeView<typename C::const_iterator> rangeView(C const& c, size_t begin, size_t end) {
  return RangeView<typename C::const_iterator>(
           std::next(c.begin(), begin),
           std::next(c.begin(), end)
         );
}

Okay, this seriously ressemble Boost.Range...

And now, let's use it!

for (auto i: rangeView(set, 1, 10)) {
  // iterate through the second to the ninth element
}

Upvotes: 9

Drew Dormann
Drew Dormann

Reputation: 63946

You can't in a set. Use the traditional for syntax or maintain your own index counter.

You can in a vector or other container with a flat layout like std::array or a C-style array. Change it to use a reference.:

for (auto &i: S)

Then you can compare the address of i with the address of s[0] to get the index.

Upvotes: 2

Seth Carnegie
Seth Carnegie

Reputation: 75150

For the general case, you'd have to use a seperate variable:

int i = 0;

for (auto x : s)
    cout << (i++ ? " " : "") << x << endl;

There are, of course, tricks for certain containers like vector, but none work for every container.

You would probably be better off using the plain for loop for this purpose.

Upvotes: 2

Armen Tsirunyan
Armen Tsirunyan

Reputation: 133112

No, you can't.

for (... : ...)

is called for instead of foreach only for the reason of not introducing a new keyword. The whole point of foreach is a quick short syntax for iterating all elements without caring for their index. For all other situations there's simple for which serves its purpose quite effectively.

Upvotes: 2

Related Questions