range-based for in c++11

22,335

Solution 1

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.

Solution 2

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
}

Solution 3

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.

Solution 4

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.

Solution 5

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.

Share:
22,335
Farzam
Author by

Farzam

Updated on May 21, 2020

Comments

  • Farzam
    Farzam about 4 years

    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.

  • Matthieu M.
    Matthieu M. over 12 years
    @Xeo: I wish we had more of them :) Unfortunately some views are simple (like this) but "transformation" views can get really complicated. They fit well in functional languages (with immutability) but the "reference" idea is harder than necessary to implement on transformations (binding to temporaries issues...). That and iterators soon get fat.
  • MSalters
    MSalters over 12 years
    What's stopping you from doing the same with a std::set? for(auto &i: S) { if (&i == &*S.begin()) {
  • Drew Dormann
    Drew Dormann over 12 years
    @MSalters: I think OP is asking for the index of any i, e.g. to detect if i was in the "first half" of the set. You are correct that you can test for the special-case of index 0.
  • MSalters
    MSalters over 12 years
    I understood it was specifically the first, as the intent is to print N-1 separators between N elements.
  • Drew Dormann
    Drew Dormann over 12 years
    @MSalters: It does seem to change focus halfway through the question.
  • bcmpinc
    bcmpinc over 10 years
    Interesting, that means that __begin and __end must be of the same type.
  • Damon
    Damon over 10 years
    @bcmpinc: But could they ever be of different type? You can only iterate over an array or over a templated container (well no, technically you can iterate over anything that ::begin() and ::end() have overloads for... but you get what I mean), so all elements including __begin and __end are of the same type anyway. They might be pointers to derived classes, yes... but even that is the same type, even if one or the other pointer points at "something different".
  • bcmpinc
    bcmpinc over 10 years
    With a filtering forward-iterator (that iterates over a collection, but only visits values that satisfy a predicate) it is much easier to for the iterator to check whether it is finished (it has to do so anyway) than to compare it to an end-iterator. By defining end() to return some different dummy type, operator!= can be implemented such that it only checks whether the iterator is finished. (I recently implemented such iterator.)