const_iterator

Discussion in 'C++' started by john smith, Aug 5, 2003.

  1. john smith

    john smith Guest

    Hi, I'm a little confused as to why the following code generates and error
    when compiling:

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

    using namespace std;

    void f(vector<int>::const_iterator& b, vector<int>::const_iterator& e)
    // compiler complains about this line
    {
    copy(b, e, ostream_iterator<int>(cout, " "));
    }
    int main()
    {
    vector<int> v1;
    back_insert_iterator<vector<int> > bii(v1);
    *bii++ = 10;
    *bii++ = -2;
    *bii++ = 5;
    f(v1.begin(), v1.end()); // compiler complains about this.
    return 0;
    }

    But I change the function definition to const vector<int>::const_iterator,
    the program compiles and runs fine. Also, the copy statement also works. I
    had always thought that if you put a const keyword before a type, then that
    means you are not going to change that type, and I thought that
    const_iterators mean that the contents of the container cannot be changed.
    So, the combination of const and const_iterator means I have an iterator
    that can't be changed, that points to a container that the contents cannot
    be changed. However, the copy line works, but copy increments the iterator
    b. So, now I'm confused about the use of const and iterators. Thanks in
    advance for your help.

    Smith
    john smith, Aug 5, 2003
    #1
    1. Advertising

  2. john smith

    Mike Wahler Guest

    john smith <> wrote in message
    news:bgn33e$109a$...
    > Hi, I'm a little confused as to why the following code generates and error
    > when compiling:
    >
    > #include <iostream>
    > #include <vector>
    > #include <iterator>


    #include <algorithm> /* for 'std::copy' */

    >
    > using namespace std;
    >
    > void f(vector<int>::const_iterator& b, vector<int>::const_iterator& e)
    > // compiler complains about this line
    > {
    > copy(b, e, ostream_iterator<int>(cout, " "));
    > }
    > int main()
    > {
    > vector<int> v1;
    > back_insert_iterator<vector<int> > bii(v1);
    > *bii++ = 10;
    > *bii++ = -2;
    > *bii++ = 5;
    > f(v1.begin(), v1.end()); // compiler complains about this.


    You're trying to bind a non-const reference to a
    non-lvalue. This is not allowed.

    > return 0;
    > }
    >
    > But I change the function definition to const vector<int>::const_iterator,
    > the program compiles and runs fine.


    Right, you changed the parameter to be a const reference,
    which can be bound to the non-lvalue (the return value
    of 'begin()' and 'end()'.)

    >Also, the copy statement also works.


    You really should have got a compiler diagnostic, since
    you didn't #include <algorithm>

    > I
    > had always thought that if you put a const keyword before a type, then

    that
    > means you are not going to change that type,


    No that's not what it means. It means you cannot change
    the *object* which is qualified with 'const'.

    >and I thought that
    > const_iterators mean that the contents of the container cannot be changed.


    No, that's not what it means. It means that the dereferenced
    iterator cannot be used to modify what it refers to.

    > So, the combination of const and const_iterator means I have an iterator
    > that can't be changed,


    No, that's not what that means. You *can* change the iterator.
    It's what it refers to that cannot be changed. I know the
    name 'const_iterator' makes this issue rather confusing to
    many folks. :)

    >that points to a container that the contents cannot
    > be changed.


    Nope. The container object (the vector) is not const
    (You didn't qualify it with the 'const' keyword.


    >However, the copy line works, but copy increments the iterator
    > b.


    Yeah, so? The first two arguments to 'copy' represent
    the input. No changes are made via those iterators.
    The 'writing' happens to the stream ('cout' in your code).

    > So, now I'm confused about the use of const and iterators. Thanks in
    > advance for your help.


    A const iterator disallows modification of the object to
    which it refers. A nonconst iterator allows modification
    of the object to which it refers.

    Nicolai Josuttis' "The C++ Standard Library" has very
    good explanations and examples of how containers and
    iterators work. www.josuttis.com/libbook

    -Mike
    Mike Wahler, Aug 5, 2003
    #2
    1. Advertising

  3. john smith wrote:
    > ...
    > #include <iostream>
    > #include <vector>
    > #include <iterator>
    >
    > using namespace std;
    >
    > void f(vector<int>::const_iterator& b, vector<int>::const_iterator& e)
    > // compiler complains about this line
    > {
    > copy(b, e, ostream_iterator<int>(cout, " "));
    > }
    > int main()
    > {
    > vector<int> v1;
    > back_insert_iterator<vector<int> > bii(v1);
    > *bii++ = 10;
    > *bii++ = -2;
    > *bii++ = 5;
    > f(v1.begin(), v1.end()); // compiler complains about this.
    > return 0;
    > }
    >
    > But I change the function definition to const vector<int>::const_iterator,
    > the program compiles and runs fine.


    Did you change parameter declarations to 'const
    vector<int>::const_iterator' or to 'const vector<int>::const_iterator&'?

    Anyway, the original code does not compile, because you are trying to
    initialize a reference of type 'vector<>::const_iterator&' (parameters
    of function 'f') with an object of type 'vector<>::iterator' (that's
    what 'v1.begin()' and 'v1.end()' return). 'const_iterator' and
    'iterator' are two completely different types. They are not
    reference-compatible, which means that the above initialization will not
    compile.

    However, type 'vector<>::iterator' is _convertible_ to type
    'vector<>::const_iterator' and the compiler will be able to take
    advantage of this conversion if the conditions are right.

    One way to make the compiler to use the conversion is to declare
    function 'f' as follows

    void f(const vector<int>::const_iterator& b,
    const vector<int>::const_iterator& e)

    Now the references refer to const-qualified types (as opposed to the
    original code) and the rules of reference initialization allow the
    compiler to convert the original 'vector<>::iterator' values to
    temporary object of type 'vector<>::const_iterator' and bind the
    references to these temporary objects.

    Another way to make the compiler to use the aforementioned conversions
    is declare function 'f' as follows

    void f(vector<int>::const_iterator b,
    vector<int>::const_iterator e)

    or

    void f(const vector<int>::const_iterator b,
    const vector<int>::const_iterator e)

    Whether you add an additional 'const' qualifier to each parameter
    declaration doesn't really make any significant difference in this context.

    You can also avoid the 'vector<>::iterator' to
    'vector<int>::const_iterator' completely by calling 'const' versions of
    'vector's 'begin()' and 'end()' methods (you original code calls
    non-const ones). In order to call the 'const' versions of these methods
    you need to do something like this

    ...
    f(const_cast<const vector<int>&>(v1).begin(),
    const_cast<const vector<int>&>(v1).end());
    ...

    although the above is far from being elegant. Note, that this
    modification alone will not make you original code to compile because of
    a related reference initialization problem: methods 'begin()' and
    'end()' return temporary objects and temporary objects cannot be used as
    initializers for non-const-qualified references (parameters of function
    'f'). You'll still need to change the declaration of 'f' in one of the
    above ways in order to fix your code.

    > Also, the copy statement also works. I
    > had always thought that if you put a const keyword before a type, then that
    > means you are not going to change that type,


    Strictly speaking, that means that you are not going to change _objects_
    of that type.

    There are also other implications caused by adding a 'const' to a
    declaration of an object. In particular, it plays an important role in
    initialization of references. You'll be better off reading about in a
    book or in C++ FAQ.

    Incorrect initialization of references is the actual problem that
    prevents your original code from compiling.

    > and I thought that
    > const_iterators mean that the contents of the container cannot be changed.
    > So, the combination of const and const_iterator means I have an iterator
    > that can't be changed, that points to a container that the contents cannot
    > be changed.


    Yes, that's true. More precisely "... that points to a container that
    the contents cannot be changed _through_ _this_ _particular_ _iterator_".

    > However, the copy line works, but copy increments the iterator
    > b.


    No, it doesn't. 'std::copy' takes it parameters _by_ _value_, which
    means that this function makes local copies if arguments (iterators)
    passed to it. The function can increment or otherwise modify these
    copies, while the original 'b' will remain unchanged.

    --
    Best regards,
    Andrey Tarasevich
    Brainbench C and C++ Programming MVP
    Andrey Tarasevich, Aug 5, 2003
    #3
  4. john smith

    john smith Guest


    > Did you change parameter declarations to 'const
    > vector<int>::const_iterator' or to 'const vector<int>::const_iterator&'?


    I changed it from vector<int>::const_iterator& to const
    vector<int>::const_iterator&

    > void f(const vector<int>::const_iterator& b,
    > const vector<int>::const_iterator& e)


    ....snip...

    > Another way to make the compiler to use the aforementioned conversions
    > is declare function 'f' as follows
    >
    > void f(vector<int>::const_iterator b,
    > vector<int>::const_iterator e)
    >


    why would this be okay when a reference is not used? Is it just because a
    local iterator is used? So
    void f(vector<int>::const_iterator& b, vector<int>::const_iterator& e) would
    not work, where as the code above works...
    why?

    > or
    >
    > void f(const vector<int>::const_iterator b,
    > const vector<int>::const_iterator e)


    Okay, now the question I have is... if I have some general function that
    take 2 iterators to denote an iterval to do something to each element in
    that interval, should the function prototype take a reference to the
    iterators or make a copy?

    >
    > Whether you add an additional 'const' qualifier to each parameter
    > declaration doesn't really make any significant difference in this

    context.
    > although the above is far from being elegant. Note, that this
    > modification alone will not make you original code to compile because of
    > a related reference initialization problem: methods 'begin()' and
    > 'end()' return temporary objects and temporary objects cannot be used as
    > initializers for non-const-qualified references (parameters of function
    > 'f'). You'll still need to change the declaration of 'f' in one of the
    > above ways in order to fix your code.


    Okay, I guess this answers the question above. Is there a reason for this
    rule?

    > You'll be better off reading about in a
    > book or in C++ FAQ.


    indeed, time to reread the section on const in stroustroup's book.

    >
    > Incorrect initialization of references is the actual problem that
    > prevents your original code from compiling.
    >
    > > and I thought that
    > > const_iterators mean that the contents of the container cannot be

    changed.
    > > So, the combination of const and const_iterator means I have an iterator
    > > that can't be changed, that points to a container that the contents

    cannot
    > > be changed.

    >
    > Yes, that's true. More precisely "... that points to a container that
    > the contents cannot be changed _through_ _this_ _particular_ _iterator_".
    >
    > > However, the copy line works, but copy increments the iterator
    > > b.

    >
    > No, it doesn't. 'std::copy' takes it parameters _by_ _value_, which
    > means that this function makes local copies if arguments (iterators)
    > passed to it. The function can increment or otherwise modify these
    > copies, while the original 'b' will remain unchanged.


    thank you very much.

    >
    > --
    > Best regards,
    > Andrey Tarasevich
    > Brainbench C and C++ Programming MVP
    >
    john smith, Aug 5, 2003
    #4
  5. john smith wrote:
    > ...
    >> Another way to make the compiler to use the aforementioned conversions
    >> is declare function 'f' as follows
    >>
    >> void f(vector<int>::const_iterator b,
    >> vector<int>::const_iterator e)
    >>

    >
    > why would this be okay when a reference is not used? Is it just because a
    > local iterator is used?


    Yes. In this case parameters 'a' and 'b' are local objects inside
    function 'f'. The compiler can initialize these parameters with
    arguments of type 'vector<>::iterator' because type 'vector<>::iterator'
    is convertible to type 'vector<>::const_iterator' (this is required by
    the standard). The compiler performs the conversion implicitly.

    For the very same reason the following code will compile:

    vector<int> v1;
    vector<int>::const_iterator cit = v1.begin(); // OK

    > So
    > void f(vector<int>::const_iterator& b, vector<int>::const_iterator& e) would
    > not work, where as the code above works...
    > why?


    Because in C++ a non-constant reference of unqualified type, say, 'T&'
    can only be bound _directly_ to its initializer. This means that the
    initializer must either

    1) have the same unqualified type 'T' and be an lvalue, or
    2) be convertible to an lvalue of unqualified type 'T'.

    In your case the types are _different_: you are trying to initialize a
    reference of type 'vector<>::const_iterator&' with an initializer of
    type 'vector<>::iterator', so (1) is not an option. The only hope that's
    left in this situation is that 'vector<>::iterator' is convertible to an
    _lvalue_ of type 'vector<>::const_iterator' - (2). But unfortunately it
    is not. Therefore the code will not compile.

    For the very same reason the following code will fail to compile as well:

    vector<int> v1;
    vector<int>::const_iterator& cit = v1.begin(); // ERROR

    >> or
    >>
    >> void f(const vector<int>::const_iterator b,
    >> const vector<int>::const_iterator e)

    >
    > Okay, now the question I have is... if I have some general function that
    > take 2 iterators to denote an iterval to do something to each element in
    > that interval, should the function prototype take a reference to the
    > iterators or make a copy?


    For input parameters you have a choice of

    1) taking a copy
    2) taking a constant reference (i.e. a reference to a 'const' object)

    In the end, it is your decision. Note, that you will be able to modify a
    local copy inside the function, but you can't modify an object passed by
    constant reference. As you probably noticed, STL normally follows the
    variant (1) (like 'std::copy' in your code). With "heavier" objects the
    variant (2) might be preferable.

    --
    Best regards,
    Andrey Tarasevich
    Brainbench C and C++ Programming MVP
    Andrey Tarasevich, Aug 5, 2003
    #5
  6. "john smith" <> wrote in message news:<bgn33e$109a$>...
    > Hi, I'm a little confused as to why the following code generates and error
    > when compiling:
    >
    > #include <iostream>
    > #include <vector>
    > #include <iterator>
    >
    > using namespace std;
    >
    > void f(vector<int>::const_iterator& b, vector<int>::const_iterator& e)
    > // compiler complains about this line
    > {
    > copy(b, e, ostream_iterator<int>(cout, " "));
    > }
    > int main()
    > {
    > vector<int> v1;
    > back_insert_iterator<vector<int> > bii(v1);
    > *bii++ = 10;
    > *bii++ = -2;
    > *bii++ = 5;
    > f(v1.begin(), v1.end()); // compiler complains about this.


    You should always include - at least part of - the actual error message.

    I'm quite sure it is something like "cannot bind temporary to non-const
    reference", but I'd prefer to be absolutely sure.

    The problem is that in the call to f the returned values of v1.begin() and
    end are temporaries, and this is only allowed if f takes them either by
    value or by const reference.

    > return 0;
    > }
    >
    > But I change the function definition to const vector<int>::const_iterator,


    I suppose you mean "const vector<int>::const_iterator &" (note the &)?

    > the program compiles and runs fine.


    As it should.

    > Also, the copy statement also works.


    Ditto.

    > I had always thought that if you put a const keyword before a type, then
    > that means you are not going to change that type,


    You mean "not going to change the declared object of that type" don't you?

    > and I thought that
    > const_iterators mean that the contents of the container cannot be changed.


    More precicely, you cannot change them via the const_iterator, i.e. you
    can neither assign through the iterator (*b = ...) nor call a non-const
    member function of the pointed-to object (b->changeSomehow();).

    > So, the combination of const and const_iterator means I have an iterator
    > that can't be changed, that points to a container that the contents cannot
    > be changed.


    You understand correctly.

    > However, the copy line works,


    Yes.

    > but copy increments the iterator b.


    No!

    Consider:

    ... ::iterator b = v1.begin();
    ... ::iterator e = v1.end();

    copy(b,e, ... );

    sort(b,e); //Surely you want to sort the entire vector here!?!

    The algorithms - including sort - take their iterator argument by value,
    so what is incremented is a copy of the const const_iterator.
    (There would no point in taking the argument by reference, since the
    algorithm would have to make a copy anyway. And iterators are supposed
    to be cheap-to-copy according to stl philosophy.)

    > So, now I'm confused about the use of const and iterators. Thanks in
    > advance for your help.
    >
    > Smith


    hth

    Uwe
    Uwe Schnitker, Aug 5, 2003
    #6
    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. Tim Partridge
    Replies:
    5
    Views:
    421
    Buster Copley
    Jul 24, 2003
  2. CoolPint
    Replies:
    3
    Views:
    719
    CoolPint
    Dec 13, 2003
  3. Gernot Frisch

    incrementiing const_iterator

    Gernot Frisch, Sep 15, 2004, in forum: C++
    Replies:
    4
    Views:
    548
    Mike Wahler
    Sep 15, 2004
  4. Joseph Turian
    Replies:
    5
    Views:
    292
    Victor Bazarov
    Aug 2, 2005
  5. Replies:
    2
    Views:
    357
    Ferdi Smit
    Oct 23, 2005
Loading...

Share This Page