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

C++ question

62 views
Skip to first unread message

jacobnavia

unread,
Jun 30, 2018, 2:09:51 PM6/30/18
to
I have the following C like C++ code:

for (int i=0; i<v.size(); i++) {
if (i&1) continue; // Treat only pair pixels
// Processing...
}

Well, ashamed of my lack of fluency in C++, I thought about porting that
code to the new syntax. Then, I came to meet the "range" definition.

Range-based for loop see
https://en.cppreference.com/w/cpp/language/range-for

I read all that, and see that in the new syntax, apparently there is no
way to access the index of the loop, i.e. the index of the object you
are working on.

In the "C like" code above the index is easily accessible. No big deal.

But with the new syntax, we have __begin and __bound, but we haven't
__index, so there is no way to do that C like code with the new syntax.

We lost the index with the new syntax.

Or I am just wrong and I have overlooked something?

Bo Persson

unread,
Jun 30, 2018, 2:39:06 PM6/30/18
to
On 2018-06-30 20:09, jacobnavia wrote:
> I have the following C like C++ code:
>
> for (int i=0; i<v.size(); i++) {
>     if (i&1) continue; // Treat only pair pixels
>     // Processing...
> }
>
> Well, ashamed of my lack of fluency in C++, I thought about porting that
> code to the new syntax. Then, I came to meet the "range" definition.
>
> Range-based for loop see
> https://en.cppreference.com/w/cpp/language/range-for
>
> I read all that, and see that in the new syntax, apparently there is no
> way to access the index of the loop, i.e. the index of the object you
> are working on.

The range-based for is intended for cases where you want to process the
entire range. It also works for cases where indexing using [i] is not
available, like a linked list.

>
> In the "C like" code above the index is easily accessible. No big deal.

You can still use that syntax, if it matches your use case better.

Otherwise, if v is a vector or array and you use

for (auto& element : v)

you can still work out the index, if you absolutely need it, by
computing the distance between element and the start of the vector.

Simple C pointer arithmetic. :-)


Bo Persson

jacobnavia

unread,
Jun 30, 2018, 2:44:00 PM6/30/18
to
Le 30/06/2018 à 20:38, Bo Persson a écrit :
> you can still work out the index, if you absolutely need it, by
> computing the distance between element and the start of the vector.

Great!

I didn't thought about that!

Of course

i = (&v - __begin)/sizeof(v[0]);

gives you the index.

Alf P. Steinbach

unread,
Jun 30, 2018, 3:25:38 PM6/30/18
to
On 30.06.2018 20:09, jacobnavia wrote:
> I have the following C like C++ code:
>
> for (int i=0; i<v.size(); i++) {
>     if (i&1) continue; // Treat only pair pixels
>     // Processing...
> }
>
> Well, ashamed of my lack of fluency in C++, I thought about porting that
> code to the new syntax. Then, I came to meet the "range" definition.
>
> Range-based for loop see
> https://en.cppreference.com/w/cpp/language/range-for
>
> I read all that, and see that in the new syntax, apparently there is no
> way to access the index of the loop, i.e. the index of the object you
> are working on.
>
> In the "C like" code above the index is easily accessible. No big deal.
>
> But with the new syntax, we have __begin and __bound, but we haven't
> __index, so there is no way to do that C like code with the new syntax.

These things starting with double underscore are implementation
artifacts. They're not portable.


> We lost the index with the new syntax.

That depends.

When you iterate over a contiguous sequence of items like the buffer of
`std::vector` or `std::string`, then you can simply compute the index
from the object address.

For other collections you can simply add an index variable that you
update in each iteration, but then most of the point of the range based
loop is lost.

The Python solution is to iterate over pairs of index+objectref,
provided by a function called `enumerate`:


values = ('a', 'b', 'c', 'd', 'e');
for index, value in enumerate( values ):
print( "%d: %c" % (index, value) )

One can do that also in C++, but the standard library doesn't provide
it. However, the Boost library provides a nice index generator.

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

template< class Type >
auto enumerate( const Type& c ) { return boost::adaptors::index( c ); }

auto main()
-> int
{
const auto values = {'a', 'b', 'c', 'd', 'e'};
for( const auto& item : enumerate( values ) )
{
using namespace std;
cout << item.index() << ": " << item.value() << endl;
}
}


> Or I am just wrong and I have overlooked something?

Yes, the Boost library.

Possibly the Ranges library also provides this functionality, but I
didn't find that just by looking around in the docs.


Cheers!,

- Alf

Paavo Helde

unread,
Jun 30, 2018, 5:04:44 PM6/30/18
to
Not sure if this is irony or not. Anyway, in pure C++ code sizeof()
should be never needed, C++ knows the size of your types:

for (auto& element : v) {
std::ptrdiff_t index = &element-&*v.begin();
}

Still not recommended as it is ugly and works only for contiguous
containers. If you need an index make a for loop with an index.

Juha Nieminen

unread,
Jul 1, 2018, 2:18:55 PM7/1/18
to
jacobnavia <ja...@jacob.remcomp.fr> wrote:
> I have the following C like C++ code:
>
> for (int i=0; i<v.size(); i++) {
> if (i&1) continue; // Treat only pair pixels
> // Processing...
> }
>
> Well, ashamed of my lack of fluency in C++, I thought about porting that
> code to the new syntax. Then, I came to meet the "range" definition.

There's absolutely nothing wrong in iterating a container using
an "old-style" for statement. If you need the index value, do it
like that. A range-based for exists as a convenience if you just
need to traverse the entire container and don't need an index.

Btw, it's not safe to compare an int to size_t. (This is true
also in C.) Unless you are absolutely sure the size is small
enough.

(Also, if you really need to do something only to each second
element, why not increment the loop variable by 2?)

Vir Campestris

unread,
Jul 2, 2018, 1:40:38 PM7/2/18
to
Use the old syntax. Then you can type i +=2 to skip the alternate ones.

Andy

jacobnavia

unread,
Jul 2, 2018, 3:29:18 PM7/2/18
to
Le 01/07/2018 à 23:40, Vir Campestris a écrit :
> Use the old syntax. Then you can type i +=2 to skip the alternate ones.

Why is the index not included as info with the new syntax?

Yes, you can figure it out, as several people pointed me to a simple
solution, so its not a big deal.

Of course you can do i += 2, but I ws thinking in terms of a filter
going through all records according to some criteria. An easy criteria
was using the index, but that was a very bad example.

Thanks

Ian Collins

unread,
Jul 2, 2018, 4:39:30 PM7/2/18
to
On 03/07/18 07:29, jacobnavia wrote:
> Le 01/07/2018 à 23:40, Vir Campestris a écrit :
>> Use the old syntax. Then you can type i +=2 to skip the alternate ones.
>
> Why is the index not included as info with the new syntax?

Not everything that can be iterated over has an index, sets and maps for
example.

--
Ian.

Manfred

unread,
Jul 3, 2018, 9:36:20 AM7/3/18
to
On 7/2/2018 9:29 PM, jacobnavia wrote:
> Le 01/07/2018 à 23:40, Vir Campestris a écrit :
>> Use the old syntax. Then you can type i +=2 to skip the alternate ones.
>
> Why is the index not included as info with the new syntax?
>
> Yes, you can figure it out, as several people pointed me to a simple
> solution, so its not a big deal.

Since you are calling the range-for the "new syntax", maybe it is worth
recalling that the "range-based for" statement (as the standard defines
it) it is an /alternative/ formulation of the for loop additional to the
for(init; condition; update){ } one, and not meant to be a replacement
or obsolete the traditional syntax.

From this perspective, it is /intentional/ that the loop variable is
not present in the range-for expression: the intent is that given a
collection, some block of instructions is executed on each element of
the collection - hence the syntax only involving the collection and a
declaration of the element to be acted upon during each iteration.

Moreover, the syntax itself only allows for iteration over /all/
elements in the collection, and not even the ordering of the traversal
is explicit in the expression - it is written in the standard that it
runs from begin() to end(), not in the coding.

So, as other have said, if the loop variable is actually instrumental to
processing in the loop, then probably the traditional syntax is a better
choice.

Out of curiosity, below are a few alternatives, the last one being the
suggestion from Alf, with the addition of an index filter:


=================================

#include <iostream>
#include <vector>
#include <boost/range/adaptor/indexed.hpp>
#include <boost/range/adaptor/filtered.hpp>

template< typename Range >
auto enumerate( const Range& r ) { return boost::adaptors::index( r ); }

template< typename Range >
bool is_even(const typename boost::range_value<Range>::type& x) { return
x.index()%2 == 0; }


template< class Type >
auto filter_even( const Type& c ) { return boost::adaptors::filter( c,
is_even<Type> ); }


int main()
{
std::vector<wchar_t> values{ L'a', L'b', L'c', L'd', L'e' };

// your original
for (int i=0; i<values.size(); ++i)
{
if (i&1) continue;
std::wcout << values[i] << std::endl;
}

std::wcout << std::endl;

// minimal loop, avoiding anything like __begin, __end ...
for (const auto& c: values)
{
if ((&c - &(values[0])) & 1 ) continue;
std::wcout << c << std::endl;
}

std::wcout << std::endl;

{
// explicit iterator, with a flag
bool skip = false;
for (auto it = values.cbegin(); it != values.cend(); ++it)
{
if (!(skip = !skip)) continue;
std::wcout << (*it) << std::endl;
}
}

std::wcout << std::endl;

// the example from Alf, with a filtered index
for( const auto& item : filter_even( enumerate( values ) ) )
{
std::wcout << item.index() << ": " << item.value() << std::endl;
}
}

=================================

>
> Of course you can do i += 2, but I ws thinking in terms of a filter
> going through all records according to some criteria. An easy criteria
> was using the index, but that was a very bad example.
See above for a boost filter

>
> Thanks
>

Juha Nieminen

unread,
Jul 4, 2018, 3:12:56 AM7/4/18
to
jacobnavia <ja...@jacob.remcomp.fr> wrote:
> Le 01/07/2018 à 23:40, Vir Campestris a écrit :
>> Use the old syntax. Then you can type i +=2 to skip the alternate ones.
>
> Why is the index not included as info with the new syntax?

A range-based for loop uses, as its name implies, an iterator range.
An iterator range can cover more than just random access data containers
(for which an "index" value makes sense).

You could have an integral *counter* that tells how many elements have
been processed so far, but that's not an actual "index" value categorically
speaking.
0 new messages