Comma operator in for loop

R

Ralf Goertz

Hi,

I guess I don't really understand the concept of commas as sequence
points but now I am in a position where I might have use for the comma
in a statement other than a declaration. Is the following legal:


#include <iostream>
#include <set>

using namespace std;


int main() {
set<unsigned> s;
s.insert(42);
s.insert(84);
set<unsigned>::iterator i;
for (i=s.begin(),++i;i!=s.end();++i)
cout<<*i<<endl;
return 0;
}

It compiles and does what I want, but it could still be undefined
behaviour. So is it okay to use ",++i" before the first ";" of the
for-loop, or is it saver to check for i!=s.begin() in the *body* of the
loop?
 
V

Victor Bazarov

I guess I don't really understand the concept of commas as sequence
points but now I am in a position where I might have use for the comma
in a statement other than a declaration. Is the following legal:


#include<iostream>
#include<set>

using namespace std;


int main() {
set<unsigned> s;
s.insert(42);
s.insert(84);
set<unsigned>::iterator i;
for (i=s.begin(),++i;i!=s.end();++i)
cout<<*i<<endl;
return 0;
}

It compiles and does what I want, but it could still be undefined
behaviour. So is it okay to use ",++i" before the first ";" of the
for-loop, or is it saver to check for i!=s.begin() in the *body* of the
loop?

It seems fine, and if it does what you need, keep it. If you just
needed to skip the first element, I'd probably do

i = ++(s.begin());

The call to 'begin' yields a temporary iterator, which you can increment
(provided the container is not empty), and then assign the value to the
'i'. The only danger is that you really cannot do the increment if the
container is empty.

V
 
M

Marc

Victor said:
++(s.begin())

Although it works here (s is a set, so the iterator can hardly be a
basic type like a pointer), I believe it is a good habit to use
boost::next and pretend the signature of operator++ is:
Iterator& operator++()&; // C++0x
 
V

Vladimir Jovic

Victor said:
It seems fine, and if it does what you need, keep it. If you just
needed to skip the first element, I'd probably do

i = ++(s.begin());

I would do this:
for (i=s.begin() + 1;i!=s.end();++i)
cout<<*i<<endl;
but it's not much different from your solution.
The call to 'begin' yields a temporary iterator, which you can increment
(provided the container is not empty), and then assign the value to the
'i'. The only danger is that you really cannot do the increment if the
container is empty.

Yes, a check is missing, but I guess in the short example as above it is
ok, since the set is not empty.
 
R

Ralf Goertz

Victor said:
It seems fine, and if it does what you need, keep it. If you just
needed to skip the first element, I'd probably do

i = ++(s.begin());

Okay, that's better since it allows me to define i in the loop.
The call to 'begin' yields a temporary iterator, which you can increment
(provided the container is not empty), and then assign the value to the
'i'. The only danger is that you really cannot do the increment if
the container is empty.

I think I can be pretty sure that s is not empty as it is in an
unordered_map<unsigned, set<unsigned> > m, and elements get inserted
into m by means of m[4711].insert(42) only.

Thanks,

Ralf
 
R

Ralf Goertz

Vladimir said:
I would do this:
for (i=s.begin() + 1;i!=s.end();++i)
cout<<*i<<endl;
but it's not much different from your solution.

That was my first idea, but it doesn't compile:

error: no match for 'operator+' in 's.std::set<_Key, _Compare, _Alloc>:
:begin [with _Key = unsigned int, _Compare = std::less<unsigned int>, _Alloc = s
td::allocator<unsigned int>]() + 1'
 
V

Victor Bazarov

Vladimir said:
I would do this:
for (i=s.begin() + 1;i!=s.end();++i)
cout<<*i<<endl;
but it's not much different from your solution.

That was my first idea, but it doesn't compile:

error: no match for 'operator+' in 's.std::set<_Key, _Compare, _Alloc>:
:begin [with _Key = unsigned int, _Compare = std::less<unsigned int>, _Alloc = s
td::allocator<unsigned int>]() + 1'

The set iterator is not of the random-access kind, you cannot add a
number to it.

V
 
V

Vladimir Jovic

Ralf said:
Vladimir said:
I would do this:
for (i=s.begin() + 1;i!=s.end();++i)
cout<<*i<<endl;
but it's not much different from your solution.

That was my first idea, but it doesn't compile:

error: no match for 'operator+' in 's.std::set<_Key, _Compare, _Alloc>:
:begin [with _Key = unsigned int, _Compare = std::less<unsigned int>, _Alloc = s
td::allocator<unsigned int>]() + 1'

Yes, off course. set::begin() returns bidirectional iterator, which do
not support arithmetic operations + and - :(
 
J

James Kanze

It seems fine, and if it does what you need, keep it. If you just
needed to skip the first element, I'd probably do
i = ++(s.begin());
The call to 'begin' yields a temporary iterator, which you can
increment (provided the container is not empty),

And provided the implementation allows it. Normally, you cannot
apply ++ to a temporary. For a user defined operator, however,
the rules of function calls apply: if operator++ is a member,
you can use it; if it is not, then you'd be binding a temporary
to a reference---which is illegal.

In practice, the need for such things occurs often enough that
most people have a template function "next":

template<typename ForwardIterator>
ForwardIterator
next(ForwardIterator original)
{
++ original;
return original;
}

and then use i = next(s.begin()).

(Of course, none of this is valid if the container is empty.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,008
Latest member
HaroldDark

Latest Threads

Top