473,386 Members | 1,712 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,386 software developers and data experts.

const_iterator

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
Jul 19 '05 #1
5 16866

john smith <as**@asdf123asdf.net> wrote in message
news:bg***********@news.eecs.umich.edu...
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

Jul 19 '05 #2
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

Jul 19 '05 #3
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

Jul 19 '05 #4
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

Jul 19 '05 #5
"john smith" <as**@asdf123asdf.net> wrote in message news:<bg***********@news.eecs.umich.edu>...
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
Jul 19 '05 #6

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

3
by: CoolPint | last post by:
If I were to design my own container and its iterators, I understand that I need to provide both "iterator" and "const_iterator" so that begin() and end() return appropriate ones depending on the...
4
by: Gernot Frisch | last post by:
void Class::work() const { for(const std::list<int>::const_iterator i = m_pts.begin(); i!=m_pts.end(); ++i); } cannot find binary operator ++ that accepts left side type of const...
5
by: John Harrison | last post by:
I there a reliable and generic method to convert a const_iterator to an iterator (i.e. something like const_cast). I ask because I'm writing some methods which take and return iterators. A const...
1
by: flopbucket | last post by:
Hi, For the learning experience, I am building a replacement for std::map. I built a templated red-black tree, etc., and all the basic stuff is working well. I implemented basic iterators and...
3
by: Bit byte | last post by:
whats the difference - apart from what the name suggests? - i.e. one is const What are there pros and cons of using one over the other ?
4
by: kotau | last post by:
Hi, I'm having trouble with something that would appear to have a simple solution. Here's a version of the code I'm working with: const Item* p 0; name::const_iterator i;
10
by: Yahooooooooo | last post by:
Hi, learning c++, can some one check below program. its giving compiler error. #include <iostream> #include <list> using namespace std; typedef list<intListInt;
2
by: subramanian100in | last post by:
Consider the code fragment: vector<intcontainer; container.push_back(0); container.push_back(1); container.push_back(2); Now I want to iterate through the 'container'. For this, instead...
2
by: Markus Dehmann | last post by:
I want to derive from std::set, like shown below. But when I try to declare an iterator over the contained elements I get an error, see the twp uncommented lines: #include <set> template<class...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.