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

Discussion in 'C++' started by Mike Austin, Oct 19, 2004.

  1. Mike Austin

    Mike Austin Guest

    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 Austin, Oct 19, 2004
    #1
    1. Advertising

  2. Mike Austin

    Mike Wahler Guest

    "Mike Austin" <> wrote in message
    news:0Lfdd.724102$...
    > 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?

    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).

    > Maybe this is a gcc issue?


    No.

    > 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


    I don't know what you mean by your comment "0",
    but note that this statement produces 'undefined behavior'.

    > cout << code.at( 10 ) << endl; // vector [] access out of range
    > } catch( out_of_range e ) {
    > cout << e.what() << endl;
    > }
    >
    > return 0;
    > }


    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, Oct 19, 2004
    #2
    1. Advertising

  3. "Mike Austin" <> wrote in message
    news:0Lfdd.724102$...
    > 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?


    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
    John Harrison, Oct 19, 2004
    #3
  4. Mike Austin

    Mike Wahler Guest

    "Mike Wahler" <> wrote in message
    news:s9gdd.1663$%...
    > > try {
    > > cout << *(iter + 5) << endl; // 0


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

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


    -Mike
    Mike Wahler, Oct 19, 2004
    #4
  5. Mike Austin

    Ron Natalie Guest

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

    Mike Austin wrote:
    > 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?


    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).

    >
    > cout << *(iter + 5) << endl; // 0


    Evaluating expression (iter + 5) is undefined beahvior.

    > cout << code.at( 10 ) << endl; // vector [] access out of range


    This is OK. At specifically throws an exception for out of range.
    Ron Natalie, Oct 19, 2004
    #5
  6. Mike Austin

    Mike Wahler Guest

    "John Harrison" <> wrote in message
    news:...
    >
    > "Mike Austin" <> wrote in message
    > news:0Lfdd.724102$...
    > > 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?

    >
    > 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.


    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
    Mike Wahler, Oct 19, 2004
    #6
  7. Mike Austin

    Adrian Guest

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

    Mike Austin wrote:
    > 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;
    > }

    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
    Adrian, Oct 19, 2004
    #7
  8. Mike Austin

    Mike Austin Guest

    "Mike Austin" <> wrote in message
    news:0Lfdd.724102$...
    > 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?


    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

    > 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 Austin, Oct 20, 2004
    #8
  9. Mike Austin

    Mike Austin Guest

    "Mike Wahler" <> wrote in message
    news:s9gdd.1663$%...
    > "Mike Austin" <> wrote in message
    > news:0Lfdd.724102$...
    > > 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?
    >
    > 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).


    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
    Mike Austin, Oct 20, 2004
    #9
  10. "Mike Wahler" <> wrote in message
    news:s9gdd.1663$%...
    > If you don't use the protection of 'vector::at()' you can still protect
    > yourself by [..snip..] comparing against 'vector::end()', etc.


    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, Oct 20, 2004
    #10
  11. "Mike Austin" <> wrote in message
    news:34hdd.724397$...
    > "Mike Austin" <> wrote in message
    > news:0Lfdd.724102$...
    >> 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?

    >
    > 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.


    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
    Ivan Vecerina, Oct 20, 2004
    #11
  12. Mike Austin

    Mike Wahler Guest

    "Ivan Vecerina" <> wrote in
    message news:cl50ik$pj5$...
    > "Mike Wahler" <> wrote in message
    > news:s9gdd.1663$%...
    > > If you don't use the protection of 'vector::at()' you can still protect
    > > yourself by [..snip..] comparing against 'vector::end()', etc.

    >
    > 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 */ ... };


    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 Wahler, Oct 20, 2004
    #12
  13. Mike Austin

    Mike Austin Guest

    "Ivan Vecerina" <> wrote in message
    news:cl511s$qep$...
    > "Mike Austin" <> wrote in message
    > news:34hdd.724397$...
    > > "iter + 0" is the selector
    > > "iter + 1" is the first argument
    > > etc.

    >
    > 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.


    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::eek:perator ++();
    }
    };

    Regards,
    Mike
    Mike Austin, Oct 21, 2004
    #13
  14. "Mike Austin" <> wrote in message
    news:s6Idd.16095$...
    > "Ivan Vecerina" <> wrote in
    > message
    > news:cl511s$qep$...

    .....
    >> 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.

    >
    > 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 {


    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).

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


    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/operators.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
    Ivan Vecerina, Oct 21, 2004
    #14
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Frederick Ding

    why always out_of_range using size_type?

    Frederick Ding, May 7, 2005, in forum: C++
    Replies:
    4
    Views:
    491
    Ron Natalie
    May 7, 2005
  2. Replies:
    8
    Views:
    1,896
    Csaba
    Feb 18, 2006
  3. Steve555
    Replies:
    3
    Views:
    10,892
    cyberscientist
    Apr 29, 2008
  4. , India
    Replies:
    2
    Views:
    370
    James Kanze
    Aug 12, 2007
  5. eric
    Replies:
    8
    Views:
    1,802
    Gert-Jan de Vos
    Jul 7, 2011
Loading...

Share This Page