range-based for in c++11
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.
Farzam
Updated on May 21, 2020Comments
-
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 firstn
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. 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 over 12 yearsWhat's stopping you from doing the same with a
std::set
?for(auto &i: S) { if (&i == &*S.begin()) {
-
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 over 12 yearsI understood it was specifically the first, as the intent is to print
N-1
separators betweenN
elements. -
Drew Dormann over 12 years@MSalters: It does seem to change focus halfway through the question.
-
bcmpinc over 10 yearsInteresting, that means that __begin and __end must be of the same type.
-
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 over 10 yearsWith 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.)