const_iterator

J

john smith

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
 
M

Mike Wahler

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

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

#include said:
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 said:
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
 
A

Andrey Tarasevich

john said:
...
#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.
 
J

john smith

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


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


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

Andrey Tarasevich

john said:
...

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 said:
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 said:
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.
 
U

Uwe Schnitker

john smith said:
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 said:
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
 

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top