Connecting Tech Pros Worldwide Forums | Help | Site Map

No out_of_range exception for "iterator + n" vs. vector.at( n )

Mike Austin
Guest
 
Posts: n/a
#1: Jul 22 '05
Hi all. Just working on a small virtual machine, and thought about using
vector iterators instead of pointer arithmetic. Question is, why does an
iterator plus any number out of range not generate a out_of_range exception?
Maybe this is a gcc issue?

I'm using gcc version 3.3.3 (cygwin special).

Here's the full sample code:

#include <iostream>
#include <vector>
#include <stdexcept>
using namespace std;

int main() {
vector<int> code;
code.push_back( 10L );
code.push_back( 20L );

vector<int>::iterator iter = code.begin();
try {
cout << *(iter + 5) << endl; // 0
cout << code.at( 10 ) << endl; // vector [] access out of range
} catch( out_of_range e ) {
cout << e.what() << endl;
}

return 0;
}


Thanks,
Mike


Mike Wahler
Guest
 
Posts: n/a
#2: Jul 22 '05

re: No out_of_range exception for "iterator + n" vs. vector.at( n )


"Mike Austin" <mike@mike-austin.com> wrote in message
news:0Lfdd.724102$Gx4.674096@bgtnsc04-news.ops.worldnet.att.net...[color=blue]
> Hi all. Just working on a small virtual machine, and thought about using
> vector iterators instead of pointer arithmetic. Question is, why does an
> iterator plus any number out of range not generate a out_of_range[/color]
exception?

Because that's the way the library is designed.
If you want range checking, use vector::at().
That's what it's for.

This follows the "don't pay for what you don't use"
principle of C++. ('at()' will necessarily add more
overhead which might be unacceptable for some
applications).
[color=blue]
> Maybe this is a gcc issue?[/color]

No.
[color=blue]
> I'm using gcc version 3.3.3 (cygwin special).
>
> Here's the full sample code:
>
> #include <iostream>
> #include <vector>
> #include <stdexcept>
> using namespace std;
>
> int main() {
> vector<int> code;
> code.push_back( 10L );
> code.push_back( 20L );
>
> vector<int>::iterator iter = code.begin();
> try {
> cout << *(iter + 5) << endl; // 0[/color]

I don't know what you mean by your comment "0",
but note that this statement produces 'undefined behavior'.
[color=blue]
> cout << code.at( 10 ) << endl; // vector [] access out of range
> } catch( out_of_range e ) {
> cout << e.what() << endl;
> }
>
> return 0;
> }[/color]

If you don't use the protection of 'vector::at()' you can still protect
yourself by checking e.g. 'vector::size()', 'vector::empty()', comparing
against 'vector::end()', etc.

-Mike


Mike Wahler
Guest
 
Posts: n/a
#3: Jul 22 '05

re: No out_of_range exception for "iterator + n" vs. vector.at( n )


"Mike Wahler" <mkwahler@mkwahler.net> wrote in message
news:s9gdd.1663$%h1.1158@newsread3.news.pas.earthl ink.net...[color=blue][color=green]
> > try {
> > cout << *(iter + 5) << endl; // 0[/color][/color]

Also note that you can use the possibly more intiutive
array notation with a vector:

iter[n]; /* but still no bounds checking */


-Mike


John Harrison
Guest
 
Posts: n/a
#4: Jul 22 '05

re: No out_of_range exception for "iterator + n" vs. vector.at( n )



"Mike Austin" <mike@mike-austin.com> wrote in message
news:0Lfdd.724102$Gx4.674096@bgtnsc04-news.ops.worldnet.att.net...[color=blue]
> Hi all. Just working on a small virtual machine, and thought about using
> vector iterators instead of pointer arithmetic. Question is, why does an
> iterator plus any number out of range not generate a out_of_range
> exception?
> Maybe this is a gcc issue?[/color]

Vector iterators are often implemented as pointers, i.e. something like

template <class T>
class vector
{
public:
typedef T* iterator;
typedef const T* const_iterator;

So vector iterators don't throw exceptions for the same reasons that
ordinary pointers don't.

john


Ron Natalie
Guest
 
Posts: n/a
#5: Jul 22 '05

re: No out_of_range exception for "iterator + n" vs. vector.at( n )


Mike Austin wrote:[color=blue]
> Hi all. Just working on a small virtual machine, and thought about using
> vector iterators instead of pointer arithmetic. Question is, why does an
> iterator plus any number out of range not generate a out_of_range exception?[/color]

It's never required to. It's undefined behavior to cause an iterator
to go outside the bounds of the array (with the exception of one past the
end).
[color=blue]
>
> cout << *(iter + 5) << endl; // 0[/color]

Evaluating expression (iter + 5) is undefined beahvior.
[color=blue]
> cout << code.at( 10 ) << endl; // vector [] access out of range[/color]

This is OK. At specifically throws an exception for out of range.
Mike Wahler
Guest
 
Posts: n/a
#6: Jul 22 '05

re: No out_of_range exception for "iterator + n" vs. vector.at( n )



"John Harrison" <john_andronicus@hotmail.com> wrote in message
news:2tlh96F21afrcU1@uni-berlin.de...[color=blue]
>
> "Mike Austin" <mike@mike-austin.com> wrote in message
> news:0Lfdd.724102$Gx4.674096@bgtnsc04-news.ops.worldnet.att.net...[color=green]
> > Hi all. Just working on a small virtual machine, and thought about[/color][/color]
using[color=blue][color=green]
> > vector iterators instead of pointer arithmetic. Question is, why does[/color][/color]
an[color=blue][color=green]
> > iterator plus any number out of range not generate a out_of_range
> > exception?
> > Maybe this is a gcc issue?[/color]
>
> Vector iterators are often implemented as pointers, i.e. something like
>
> template <class T>
> class vector
> {
> public:
> typedef T* iterator;
> typedef const T* const_iterator;
>
> So vector iterators don't throw exceptions for the same reasons that
> ordinary pointers don't.[/color]

That's possibly an 'incidental' reason for some implementations,
but imo not "the" reason, which is to allow best possible performance
by not imposing bounds-check overhead.

-Mike


Adrian
Guest
 
Posts: n/a
#7: Jul 22 '05

re: No out_of_range exception for "iterator + n" vs. vector.at( n )


Mike Austin wrote:[color=blue]
> Hi all. Just working on a small virtual machine, and thought about using
> vector iterators instead of pointer arithmetic. Question is, why does an
> iterator plus any number out of range not generate a out_of_range exception?
> Maybe this is a gcc issue?
>
> I'm using gcc version 3.3.3 (cygwin special).
>
> Here's the full sample code:
>
> #include <iostream>
> #include <vector>
> #include <stdexcept>
> using namespace std;
>
> int main() {
> vector<int> code;
> code.push_back( 10L );
> code.push_back( 20L );
>
> vector<int>::iterator iter = code.begin();
> try {
> cout << *(iter + 5) << endl; // 0
> cout << code.at( 10 ) << endl; // vector [] access out of range
> } catch( out_of_range e ) {
> cout << e.what() << endl;
> }
>
> return 0;
> }[/color]
May well be wrong, but I belive only the member function at() will throw
an out_of_range, so you need to use that to test, otherwise the compiler
has not idea what you are doing.

Adrian
Mike Austin
Guest
 
Posts: n/a
#8: Jul 22 '05

re: No out_of_range exception for "iterator + n" vs. vector.at( n )


"Mike Austin" <mike@mike-austin.com> wrote in message
news:0Lfdd.724102$Gx4.674096@bgtnsc04-news.ops.worldnet.att.net...[color=blue]
> Hi all. Just working on a small virtual machine, and thought about using
> vector iterators instead of pointer arithmetic. Question is, why does an
> iterator plus any number out of range not generate a out_of_range[/color]
exception?[color=blue]
> Maybe this is a gcc issue?[/color]

Thanks for everyone's reply. The reason I ask is that I want to point to a
index into vector, then read an offset by that. I could use code.at( ip +
offset ), but I wanted to do everything with iterators. I realize [] does
no bounds checking, but did not know that (iter + n) does not either.

"iter + 0" is the selector
"iter + 1" is the first argument
etc.

Thanks,
Mike
[color=blue]
> I'm using gcc version 3.3.3 (cygwin special).
>
> Here's the full sample code:
>
> #include <iostream>
> #include <vector>
> #include <stdexcept>
> using namespace std;
>
> int main() {
> vector<int> code;
> code.push_back( 10L );
> code.push_back( 20L );
>
> vector<int>::iterator iter = code.begin();
> try {
> cout << *(iter + 5) << endl; // 0
> cout << code.at( 10 ) << endl; // vector [] access out of range
> } catch( out_of_range e ) {
> cout << e.what() << endl;
> }
>
> return 0;
> }
>
>
> Thanks,
> Mike
>[/color]

Mike Austin
Guest
 
Posts: n/a
#9: Jul 22 '05

re: No out_of_range exception for "iterator + n" vs. vector.at( n )


"Mike Wahler" <mkwahler@mkwahler.net> wrote in message
news:s9gdd.1663$%h1.1158@newsread3.news.pas.earthl ink.net...[color=blue]
> "Mike Austin" <mike@mike-austin.com> wrote in message
> news:0Lfdd.724102$Gx4.674096@bgtnsc04-news.ops.worldnet.att.net...[color=green]
> > Hi all. Just working on a small virtual machine, and thought about[/color][/color]
using[color=blue][color=green]
> > vector iterators instead of pointer arithmetic. Question is, why does[/color][/color]
an[color=blue][color=green]
> > iterator plus any number out of range not generate a out_of_range[/color]
> exception?
>
> Because that's the way the library is designed.
> If you want range checking, use vector::at().
> That's what it's for.
>
> This follows the "don't pay for what you don't use"
> principle of C++. ('at()' will necessarily add more
> overhead which might be unacceptable for some
> applications).[/color]

Well, I don't like the way it works. I use vector so I don't have to worry
(as much) about my programs going awry.
I'd be delighted if you could help me write a "safe_iterator" class. Here's
a start:

template <typename T>
class safe_iterator : public T::iterator {
Value operator ++() {
if( *this == container->end() ) // how to access "container"?
throw out_of_range( "*** operator vector *(): out of range" );
}
};

Regards,
Mike

Ivan Vecerina
Guest
 
Posts: n/a
#10: Jul 22 '05

re: No out_of_range exception for "iterator + n" vs. vector.at( n )


"Mike Wahler" <mkwahler@mkwahler.net> wrote in message
news:s9gdd.1663$%h1.1158@newsread3.news.pas.earthl ink.net...[color=blue]
> If you don't use the protection of 'vector::at()' you can still protect
> yourself by [..snip..] comparing against 'vector::end()', etc.[/color]

Note that it is not obvious to compare against end(), because it may
be too late to avoid Undefined Behavior (UB):
iter += 5; // if result is > vect.end(), UB happens here!
if( iter >= vect.end() ) { ... /* but it's too late */ ... };

hth -Ivan
--
http://ivan.vecerina.com/contact/?subject=NG_POST <- email contact form


Ivan Vecerina
Guest
 
Posts: n/a
#11: Jul 22 '05

re: No out_of_range exception for "iterator + n" vs. vector.at( n )


"Mike Austin" <mike@mike-austin.com> wrote in message
news:34hdd.724397$Gx4.720215@bgtnsc04-news.ops.worldnet.att.net...[color=blue]
> "Mike Austin" <mike@mike-austin.com> wrote in message
> news:0Lfdd.724102$Gx4.674096@bgtnsc04-news.ops.worldnet.att.net...[color=green]
>> Hi all. Just working on a small virtual machine, and thought about using
>> vector iterators instead of pointer arithmetic. Question is, why does an
>> iterator plus any number out of range not generate a out_of_range[/color]
> exception?[color=green]
>> Maybe this is a gcc issue?[/color]
>
> Thanks for everyone's reply. The reason I ask is that I want to point to
> a
> index into vector, then read an offset by that. I could use code.at( ip +
> offset ), but I wanted to do everything with iterators. I realize [] does
> no bounds checking, but did not know that (iter + n) does not either.
>
> "iter + 0" is the selector
> "iter + 1" is the first argument
> etc.[/color]

So you would need to do a check such as:
if( vect.end()-iter < 2 ) { /* throw invalid position error */ }

Note that, being a random iterator, vector::iterator supports
the following syntax, just like native pointers:
iter[pos] // equivalent to *(iter+pos), but maybe nicer...

Finally, note that some implementations of the standard library
support a debug mode where the ranges of all iterators (and
other consistency checks) are made automatically.
(STLport is one such implementation, I do not know about gcc's).
This doesn't help write portable code, but can be helpful
during debugging.

Regards,
Ivan
--
http://ivan.vecerina.com/contact/?subject=NG_POST <- email contact form


Mike Wahler
Guest
 
Posts: n/a
#12: Jul 22 '05

re: No out_of_range exception for "iterator + n" vs. vector.at( n )


"Ivan Vecerina" <INVALID_please_use_contact_form@vecerina.com> wrote in
message news:cl50ik$pj5$1@newshispeed.ch...[color=blue]
> "Mike Wahler" <mkwahler@mkwahler.net> wrote in message
> news:s9gdd.1663$%h1.1158@newsread3.news.pas.earthl ink.net...[color=green]
> > If you don't use the protection of 'vector::at()' you can still protect
> > yourself by [..snip..] comparing against 'vector::end()', etc.[/color]
>
> Note that it is not obvious to compare against end(), because it may
> be too late to avoid Undefined Behavior (UB):
> iter += 5; // if result is > vect.end(), UB happens here!
> if( iter >= vect.end() ) { ... /* but it's too late */ ... };[/color]

I was simply listing possible functions to use, depending upon
context. I wasn't trying to imply that 'end()' could protect
against e.g. 'iter+5' where that is out of bounds. 'end()'
would of course apply to iterating via a loop. I did presume
the OP would actually read about these functions before using
them.

-Mike


Mike Austin
Guest
 
Posts: n/a
#13: Jul 22 '05

re: No out_of_range exception for "iterator + n" vs. vector.at( n )


"Ivan Vecerina" <INVALID_use_webform_instead@vecerina.com> wrote in message
news:cl511s$qep$1@newshispeed.ch...[color=blue]
> "Mike Austin" <mike@mike-austin.com> wrote in message
> news:34hdd.724397$Gx4.720215@bgtnsc04-news.ops.worldnet.att.net...[color=green]
> > "iter + 0" is the selector
> > "iter + 1" is the first argument
> > etc.[/color]
>
> So you would need to do a check such as:
> if( vect.end()-iter < 2 ) { /* throw invalid position error */ }
>
> Note that, being a random iterator, vector::iterator supports
> the following syntax, just like native pointers:
> iter[pos] // equivalent to *(iter+pos), but maybe nicer...
>
> Finally, note that some implementations of the standard library
> support a debug mode where the ranges of all iterators (and
> other consistency checks) are made automatically.
> (STLport is one such implementation, I do not know about gcc's).
> This doesn't help write portable code, but can be helpful
> during debugging.[/color]

I would like to use exceptions, rather than put code checks around
everything. It's just cleaner and more structured.
I don't know exactly how to implement 'safe_iterator', but the following is
a start. Would anyone like to give me a hint?

template <typename T>
class safe_iterator : public T::iterator {
Value operator ++() {
if( *this == container->end() ) // how to access "container"?
throw out_of_range( "operator vector ++(): out of range" );
else
T::iterator::operator ++();
}
};

Regards,
Mike

Ivan Vecerina
Guest
 
Posts: n/a
#14: Jul 22 '05

re: No out_of_range exception for "iterator + n" vs. vector.at( n )


"Mike Austin" <mike@mike-austin.com> wrote in message
news:s6Idd.16095$OD2.3270@bgtnsc05-news.ops.worldnet.att.net...[color=blue]
> "Ivan Vecerina" <INVALID_use_webform_instead@vecerina.com> wrote in
> message
> news:cl511s$qep$1@newshispeed.ch...[/color]
.....[color=blue][color=green]
>> Finally, note that some implementations of the standard library
>> support a debug mode where the ranges of all iterators (and
>> other consistency checks) are made automatically.
>> (STLport is one such implementation, I do not know about gcc's).
>> This doesn't help write portable code, but can be helpful
>> during debugging.[/color]
>
> I would like to use exceptions, rather than put code checks around
> everything. It's just cleaner and more structured.
> I don't know exactly how to implement 'safe_iterator', but the following
> is
> a start. Would anyone like to give me a hint?
>
> template <typename T>
> class safe_iterator : public T::iterator {[/color]

Derivation is unsafe here (especially once you need
to add a data member). Unfortunately you should prefer
containment (store the base iterator as a data member).
[color=blue]
> Value operator ++() {
> if( *this == container->end() ) // how to access "container"?
> throw out_of_range( "operator vector ++(): out of range" );
> else
> T::iterator::operator ++();
> }
> };[/color]

You need to store a pointer to the container within the iterator.
Then you can use it to test for valid bounds, but eventually also
for validating iterator pairs during comparisons.
For example:
template<typename T>
bool operator ==( safe_iterator<T> const& a
, safe_iterator<T> const& b )
{
if( a.pContainer != b.pContainer ) throw( .... );
return a.base == b.base;
};
[ What you cannot reliably check for this way is whether the
iterator is being used after having been invalidated
by a call modifying the contents of the container ]

It is not a trivial task to implement a complete iterator
interface, although some libraries can help with this
(http://www.boost.org/libs/utility/op....htm#iterator).
But in the end, implementing fully checked iterators
takes some real effort, and implies that many redundant
error checks will be included in the compiled code.


The philosophy of the STL is that operations are performed
on a valid range specified by two iterators. This way you
check upfront that the range is valid/has enough elements,
and then you perform all operations without further overhead.

I do not know specifically what you are implementing, but
if fully implementing a safe iterator seems excessively
complex, maybe you can try to adapt your approach to the
problem?
Alternatively, consider writing a few non-member functions
that encapsulate the essential operations for which you need
range checking.
E.g.:
template<class Cont, class Iter>
Iter checkedOffset(Cont const& c, Iter const& p, int offset)
{
if( offset>0 && (c.end() - p)<=offset ) throw(....);
if( offset<0 && (c.begin()-p)> offset ) throw(....);
return p+offset;
}

Regards,
Ivan
--
http://ivan.vecerina.com/contact/?subject=NG_POST <- email contact form
Brainbench MVP for C++ <> http://www.brainbench.com


Closed Thread