Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

auto i:v as iterator

55 views
Skip to first unread message

Ralf Goertz

unread,
Jul 16, 2015, 4:39:15 AM7/16/15
to
Hi,

is it possible to have the new auto syntax for loops and get an iterator
instead of a copy or a reference to the stored object itself? I would
love to use the new syntax, but often I need to know what number in a
vector the current object is. The following program doesn't compile.

#include <vector>
#include <iostream>

int main() {
std::vector<int> v={47,11};
for (auto i:v) {
//std::cout<<i<<std::endl;
std::cout<<distance(i,v.begin())<<endl;
}
return 0;
}


Bo Persson

unread,
Jul 16, 2015, 5:10:02 AM7/16/15
to
If you have a reference to the element (auto&...), you can compute the
difference between &i and &v[0].


Bo Persson

Ralf Goertz

unread,
Jul 16, 2015, 6:00:20 AM7/16/15
to
Am Thu, 16 Jul 2015 11:09:51 +0200
schrieb Bo Persson <b...@gmb.dk>:

> On 2015-07-16 10:39, Ralf Goertz wrote:
> > Hi,
> >
> > is it possible to have the new auto syntax for loops and get an
> > iterator instead of a copy or a reference to the stored object
> > itself? I would love to use the new syntax, but often I need to
> > know what number in a vector the current object is. The following
> > program doesn't compile.
> >
> > #include <vector>
> > #include <iostream>
> > #include <iterator>
> >
> > int main() {
> > std::vector<int> v={47,11};
> > for (auto i:v) {
> > //std::cout<<i<<std::endl;
> > std::cout<<std::distance(i,v.begin())<<std::endl;
> > }
> > return 0;
> > }
> >
> >
>
> If you have a reference to the element (auto&...), you can compute
> the difference between &i and &v[0].

Thanks, that works for vector and array, but what about lists or even
sets and maps? I guess I can't add another variable declaration within
the "for" parenthesis like

for (auto c=0, i:v) {…}

Öö Tiib

unread,
Jul 16, 2015, 8:17:30 AM7/16/15
to
On Thursday, 16 July 2015 13:00:20 UTC+3, Ralf Goertz wrote:
> Am Thu, 16 Jul 2015 11:09:51 +0200
> schrieb Bo Persson <b...@gmb.dk>:
>
> > On 2015-07-16 10:39, Ralf Goertz wrote:
> > > Hi,
> > >
> > > is it possible to have the new auto syntax for loops and get an
> > > iterator instead of a copy or a reference to the stored object
> > > itself? I would love to use the new syntax, but often I need to
> > > know what number in a vector the current object is. The following
> > > program doesn't compile.
> > >
> > > #include <vector>
> > > #include <iostream>
> > > #include <iterator>
> > >
> > > int main() {
> > > std::vector<int> v={47,11};
> > > for (auto i:v) {
> > > //std::cout<<i<<std::endl;
> > > std::cout<<std::distance(i,v.begin())<<std::endl;
> > > }
> > > return 0;
> > > }

It is hard to tell what you try to do because if i was iterator then this
code would just output 0 and -1. It does not feel like good way
for achieving such output, so there must be some sort of point to
it.

> >
> > If you have a reference to the element (auto&...), you can compute
> > the difference between &i and &v[0].
>
> Thanks, that works for vector and array, but what about lists or even
> sets and maps?

Element's reference is cheap to convert to iterator only for containers
where the elements are stored contiguously in memory (like 'std::array',
'std::vector<anything_but_bool>', 'std::valarray' or 'std::string' ) or
for intrusive containers where navigation information is part of object
(like Boost.Intrusive containers).

> I guess I can't add another variable declaration within
> the "for" parenthesis like
>
> for (auto c=0, i:v) {...}

There's no need to squeeze all things between those '(' and ')' of for loop.
That works:

int c = 0; for (auto i:v) {...}

If you need to limit scope of c for whatever reason then limit:

{int c = 0; for (auto i:v) {...}}

There are no silver bullets good for all situations. New range based for
loop for example is meant for cases when you need elements from
container (or container-like thing).

If you need iterators then use the old for loop. Behavior attempted
in your original example:

#include <vector>
#include <iostream>
#include <iterator>

int main()
{
std::vector<int> v={47,11};

auto start = begin( v );
for ( auto i = start; i != end( v ); ++i )
{
std::cout<< distance( i, start ) << std::endl;
}
return 0;
}

It may be that you need something from Boost.Range or the
like but it is hard to tell without knowing what you actually
want to achieve.

Ralf Goertz

unread,
Jul 16, 2015, 9:47:27 AM7/16/15
to
Am Thu, 16 Jul 2015 05:17:03 -0700 (PDT)
schrieb Öö Tiib <oot...@hot.ee>:

> On Thursday, 16 July 2015 13:00:20 UTC+3, Ralf Goertz wrote:

[dequoted to make the news server happy]

#include <vector> #include <iostream> #include <iterator>

int main() {
std::vector<int> v={47,11};
for (auto i:v) {
//std::cout<<i<<std::endl;
std::cout<<std::distance(i,v.begin())<<std::endl;
}
return 0;
}

> It is hard to tell what you try to do because if i was iterator then
> this code would just output 0 and -1. It does not feel like good way
> for achieving such output, so there must be some sort of point to
> it.

Sure, I was merely trying to give a non compiling example (which in the
OP turned out to be non-compilable not only because of the
auto=/=iterator problem). And of course I meant
std::distance(v.begin(),i)


> Element's reference is cheap to convert to iterator only for
> containers where the elements are stored contiguously in memory

I know that and I have to admit that I was stretching the limits. (I
actually need it only for vectors and Bo told me how to do it.) But
anyway, what is bad about the idea that the auto keyword in range based
for loops for container C can also be standing for C::iterator? The
compiler would need to check the first appearence of the (in my case) i
and deduce it is an iterator.

> > I guess I can't add another variable declaration within the "for"
> > parenthesis like
> >
> > for (auto c=0, i:v) {...}
>
> There's no need to squeeze all things between those '(' and ')' of
> for loop. That works:
>
> int c = 0; for (auto i:v) {...}
>
> If you need to limit scope of c for whatever reason then limit:
>
> {int c = 0; for (auto i:v) {...}}

But don't you agree that this approach is somewhat less elegant?

> It may be that you need something from Boost.Range or the like but it
> is hard to tell without knowing what you actually want to achieve.

Sometimes I need to handle the say 10th element of a vector differently
than the others:

for (auto &i:v) {
if (&i-&v[0]==10) {…} // special stuff
else {…} // do ordinary stuff
}

Öö Tiib

unread,
Jul 16, 2015, 11:28:14 AM7/16/15
to
On Thursday, 16 July 2015 16:47:27 UTC+3, Ralf Goertz wrote:
> Am Thu, 16 Jul 2015 05:17:03 -0700 (PDT)
> schrieb Öö Tiib <oot...@hot.ee>:
>
> > On Thursday, 16 July 2015 13:00:20 UTC+3, Ralf Goertz wrote:
>
> [dequoted to make the news server happy]
>
> #include <vector> #include <iostream> #include <iterator>
>
> int main() {
> std::vector<int> v={47,11};
> for (auto i:v) {
> //std::cout<<i<<std::endl;
> std::cout<<std::distance(i,v.begin())<<std::endl;
> }
> return 0;
> }
>
> > It is hard to tell what you try to do because if i was iterator then
> > this code would just output 0 and -1. It does not feel like good way
> > for achieving such output, so there must be some sort of point to
> > it.
>
> Sure, I was merely trying to give a non compiling example (which in the
> OP turned out to be non-compilable not only because of the
> auto=/=iterator problem). And of course I meant
> std::distance(v.begin(),i)

Ok, so what you wanted was index of element. I post example below,
later.

> > Element's reference is cheap to convert to iterator only for
> > containers where the elements are stored contiguously in memory
>
> I know that and I have to admit that I was stretching the limits. (I
> actually need it only for vectors and Bo told me how to do it.) But
> anyway, what is bad about the idea that the auto keyword in range based
> for loops for container C can also be standing for C::iterator? The
> compiler would need to check the first appearence of the (in my case) i
> and deduce it is an iterator.

C++ is hopefully remaining strongly typed language and so 'auto' is
remaining best to use where it is clear right away what type is meant.

> > > I guess I can't add another variable declaration within the "for"
> > > parenthesis like
> > >
> > > for (auto c=0, i:v) {...}
> >
> > There's no need to squeeze all things between those '(' and ')' of
> > for loop. That works:
> >
> > int c = 0; for (auto i:v) {...}
> >
> > If you need to limit scope of c for whatever reason then limit:
> >
> > {int c = 0; for (auto i:v) {...}}
>
> But don't you agree that this approach is somewhat less elegant?

No. I have always considered lot of declarations and logic between
parentheses of old 'for' cryptic. When there are lot of things then I
prefer it as 'while' instead. Range based for is good since it lets us
to write simple thing simply.

> > It may be that you need something from Boost.Range or the like but it
> > is hard to tell without knowing what you actually want to achieve.
>
> Sometimes I need to handle the say 10th element of a vector differently
> than the others:
>
> for (auto &i:v) {
> if (&i-&v[0]==10) {...} // special stuff
> else {...} // do ordinary stuff
> }

If index matters then you can index it with Boost.Range. It works
even with containers non-contiguous in memory. For example:

#include <boost/range/adaptor/indexed.hpp>
#include <iterator>
#include <iostream>
#include <set>

int main()
{
std::set<int> input = {90,80,70,60,50,40,30,20,10};

using boost::adaptors::indexed;

for ( auto const& element : input | indexed(0) )
{
if ( element.index() == 5 )
{
std::cout << "Special element = " << element.value() << std::endl;
}
else
{
std::cout << "Usual element = " << element.value() << std::endl;
}
}

return 0;
}

Output:

Usual element = 10
Usual element = 20
Usual element = 30
Usual element = 40
Usual element = 50
Special element = 60
Usual element = 70
Usual element = 80
Usual element = 90

Feels cheaper than to find out 'distance' from
start of 'set' for each iteration and safer than with
separate counter.

bartekltg

unread,
Jul 16, 2015, 12:06:56 PM7/16/15
to
std::vector<int> v={47,11,11,15,154};
for (auto & i:v) {
//std::cout<<i<<std::endl;
std::cout<<distance(v.begin() +
(&(*v.begin())-&i),v.begin())<<endl;
}
return 0;
}


Results:
0
1
2
3
4


Do NOT use it. I.m almost certain it will blow up in your face;]
Just use normal for loop. The new syntax is there for your convenience,
you are not force to use it if it is harder to write something.
Even c-like loop with int/site_t is _sometimes_ better.

bartekltg



Ralf Goertz

unread,
Jul 17, 2015, 1:57:58 AM7/17/15
to
Am Thu, 16 Jul 2015 08:27:58 -0700 (PDT)
schrieb Öö Tiib <oot...@hot.ee>:

> [boost.range program]

I like that one.

> Feels cheaper than to find out 'distance' from
> start of 'set' for each iteration and safer than with
> separate counter.

Definitely. Thanks for the suggestion. This shows that boost still has a
lot of stuff worth discovering.

Alf P. Steinbach

unread,
Jul 17, 2015, 5:18:55 PM7/17/15
to
In Python there is a built-in function `enumerated` that produces a
corresponding sequence of (index, value) pairs:

raw_array = [1.2, 3.4, 5.6]
for x in enumerate( raw_array ):
print( "%d -> %g" % (x[0], x[1]) )

How to do that in C++?

Well, Öö Tiib has already mentioned the Boost Range library's `indexed`,
and I would guess the Python `enumerate` facility served as inspiration
for that.

If you don't want a dependency on Boost, or if you just don't want the
over-engineered custom operator| thing, then it's not hard to do this
yourself, e.g. like the following -- I wrote this code in response to
your posting, so it's not been tested more than the little program:

<file "Indexer.hpp">
#pragma once
// Copyright (c) 2015 Alf P. Steinbach

#include <iterator> // std::iterator_traits
#include <stddef.h> // ptrdiff_t
#include <type_traits> // std::remove_reference
#include <utility> // std::pair, std::declval, std::begin,
std::end, std::move

namespace progrock{ namespace cppx{
using std::begin;
using std::declval;
using std::end;
using std::iterator;
using std::iterator_traits;
using std::move;
using std::pair;
using std::random_access_iterator_tag;
using std::remove_reference;

using Index = ptrdiff_t;

template< class Underlying_iter_tp, class Index_tp = Index >
class Indexer
{
public:
using Underlying_iter = Underlying_iter_tp;
using Index = Index_tp;

using Item = typename remove_reference<decltype((
*declval<Underlying_iter>() ))>::type;

struct Numbered_item
: pair<Index, Item*>
{
using pair<Index, Item*>::pair;

auto index() const -> Index { return first; }
auto value() -> Item& { return *second; }
auto value() const -> Item const& { return *second; }
};

private:
Underlying_iter under_begin_;
Underlying_iter under_end_;

class Iter
: public iterator<
typename
iterator_traits<Underlying_iter>::iterator_category,
Numbered_item,
typename iterator_traits<Underlying_iter>::difference_type,
typename iterator_traits<Underlying_iter>::pointer,
typename iterator_traits<Underlying_iter>::reference
>
{
friend class Indexer;
private:
Underlying_iter it_;
Numbered_item item_;

Iter( Underlying_iter it )
: it_( move( it ) )
, item_( 0, &*it ) // TODO: Check validity
{}

public:
friend
auto operator<( Iter const& a, Iter const& b )
-> bool
{ return (a.it_ < b.it_); }

friend
auto operator<=( Iter const& a, Iter const& b )
-> bool
{ return (a.it_ <= b.it_); }

friend
auto operator==( Iter const& a, Iter const& b )
-> bool
{ return (a.it_ == b.it_); }

friend
auto operator>=( Iter const& a, Iter const& b )
-> bool
{ return (a.it_ >= b.it_); }

friend
auto operator>( Iter const& a, Iter const& b )
-> bool
{ return (a.it_ > b.it_); }

friend
auto operator!=( Iter const& a, Iter const& b )
-> bool
{ return (a.it_ != b.it_); }

auto operator++()
-> Iter&
{
++it_;
++item_.first; item_.second = &*it_;
return *this;
}

auto operator++( int )
-> Iter
{
Iter original = *this;
++*this;
return original;
}

auto operator*()
-> Numbered_item const&
{ return item_; }
};

public:
auto begin() -> Iter { return Iter( under_begin_ ); }
auto end() -> Iter { return Iter( under_end_ ); }

Indexer( Underlying_iter const begin_iter, Underlying_iter
const end_iter )
: under_begin_( begin_iter )
, under_end_( end_iter )
{}
};

template< class Collection_tp >
auto indexed( Collection_tp& c )
-> Indexer<decltype( begin( c ) )>
{ return Indexer<decltype( begin( c ) )>( begin( c ), end( c ) ); }
}} // namespace progrock::cppx
</file>

<file "main.cpp">
#include "Indexer.hpp"
using namespace progrock;
using cppx::indexed;

#include <iostream>
#include <array>
#include <list>
#include <vector>
using namespace std;

template< class Collection >
void display( Collection const& c )
{
for( auto const item : indexed( c ) )
{
wcout << item.index() << L" → " << item.value() << endl;
}
}

auto main() -> int
{
display( array<double, 3>{1.2, 3.4, 5.6} );
wcout << endl;
display( vector<double>{1.2, 3.4, 5.6} );
wcout << endl;
display( list<double>{1.2, 3.4, 5.6} );
wcout << endl;

vector<double> v(5);
for( auto item : indexed( v ) )
{
item.value() = 0.1*item.index();
}
display( v );
}
</file>

The little arrow in the output may be displayed incorrectly or may cause
truncation of the output. One fix is to replace it with "->", which has
only ASCII characters. Another fix is to configure the C++ i/o properly,
which, sadly, is not done by default by current implementations; for
code that does this see e.g.

<url:
http://stackoverflow.com/questions/30197758/how-can-i-make-unicode-iostream-i-o-work-in-both-windows-and-unix-land>

And if you want to display that little arrow with Python in Windows
consoles, see e.g.

<url:
https://alfps.wordpress.com/2015/05/12/non-crashing-python-3-x-output-in-windows/>


Cheers & hth.,

- Alf

--
Using Thunderbird as Usenet client, Eternal September as NNTP server.
0 new messages