By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
448,814 Members | 1,671 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 448,814 IT Pros & Developers. It's quick & easy.

Tricky C++ problem...

P: n/a
I'm moving some code from VC++ 6 to VC++ 2005 and
I've run into a nasty problem because iterators
are no longer pointers.

In the program I'm moving, there's a std::vector
of items hidden inside a class and users of the
class get to iterate it via a const_iterator.

eg.

class foo {
typedef std::vector<bar>list_type;
list_type list;
public:
typedef list_type::const iterator iterator;
iterator begin() { return list.begin(); }
iterator end() { return list.end(); }
};
Then:

for (foo::iterator i=myFoo.begin(); i!=myFoo.end(); ++i) {
// ...
}

Ok, the problem:

Class foo defines a function "erase" to remove a
bar from the list, ie.:

class foo {
...
void erase(iterator i) {
}
};

How can I call std::vector::erase() if I only have
a const iterator? When iterators were pointers I
could just do a const_cast and it worked. With the
new style iterators there's no way to convert a
const iterator to non-const.

There's also no conversion of pointers to iterators,
no addition of values to iterators ("list.begin()+x"),
no nothing!

I'm stumped....
--
<\___/>
/ O O \
\_____/ FTB. For email, remove my socks.
Weíre judging how a candidate will handle a nuclear
crisis by how well his staff creates campaign ads.
Itís a completely nonsensical process.
Dec 21 '06 #1
Share this Question
Share on Google+
15 Replies


P: n/a

fungus skrev:
I'm moving some code from VC++ 6 to VC++ 2005 and
I've run into a nasty problem because iterators
are no longer pointers.
They never were in "standard-speak", so you've built on a nonportable
architecture.
>
In the program I'm moving, there's a std::vector
of items hidden inside a class and users of the
class get to iterate it via a const_iterator.

eg.

class foo {
typedef std::vector<bar>list_type;
list_type list;
public:
typedef list_type::const iterator iterator;
iterator begin() { return list.begin(); }
iterator end() { return list.end(); }
};
You could have made the class more standard looking if you had both
const and non-const iterators returned. But never mind.
>

Then:

for (foo::iterator i=myFoo.begin(); i!=myFoo.end(); ++i) {
// ...
}

Ok, the problem:

Class foo defines a function "erase" to remove a
bar from the list, ie.:

class foo {
...
void erase(iterator i) {
}
};

How can I call std::vector::erase() if I only have
a const iterator? When iterators were pointers I
could just do a const_cast and it worked. With the
new style iterators there's no way to convert a
const iterator to non-const.
There is a simple way out: calculate the distance from begin to the
iterator and get your iterator from that distance:

void erase(iterator i)
{
size_t dist = i - begin();
list_type.erase(list_type.begin + dist);
}
>
There's also no conversion of pointers to iterators,
no addition of values to iterators ("list.begin()+x"),
no nothing!
Well... that one is wrong.

/Peter

Dec 21 '06 #2

P: n/a
fungus wrote:
I'm moving some code from VC++ 6 to VC++ 2005 and
I've run into a nasty problem because iterators
are no longer pointers.
It's better to never assume them to be.
In the program I'm moving, there's a std::vector
of items hidden inside a class and users of the
class get to iterate it via a const_iterator.

eg.

class foo {
typedef std::vector<bar>list_type;
list_type list;
public:
typedef list_type::const iterator iterator;
iterator begin() { return list.begin(); }
iterator end() { return list.end(); }
};
Then:

for (foo::iterator i=myFoo.begin(); i!=myFoo.end(); ++i) {
// ...
}

Ok, the problem:

Class foo defines a function "erase" to remove a
bar from the list, ie.:

class foo {
...
void erase(iterator i) {
}
};

How can I call std::vector::erase() if I only have
a const iterator?
Hmm, the standard library seems to be a bit inconsistent here with the core
language, where deleting an object doesn't count as modification. I wonder
why you can't erase with a const_iterator. I don't know what you could do
other than changing to std::vector::iterator. Well, you could define your
own iterator class for foo that contains an std::vector::iterator and
behaves like a const_iterator.
When iterators were pointers I could just do a const_cast and it worked.
Avoid const_cast whenever you can. Its primary purpose (IMHO) is to work
around bugs in code you can't change.
With the new style iterators there's no way to convert a const iterator to
non-const.

There's also no conversion of pointers to iterators,
Iterators are a more abstract concept. They are supposed to work with other
containers as well, where they can't be represented by a simple pointer.
no addition of values to iterators ("list.begin()+x"),
Actually, there is, but only for random-access iterators (which std::vector
has). There is even operator [].

Dec 21 '06 #3

P: n/a
Fungus:
class foo {
typedef std::vector<bar>list_type;
list_type list;
public:
typedef list_type::const iterator iterator;
iterator begin() { return list.begin(); }
iterator end() { return list.end(); }
};
The line:

typedef list_type::const iterator iterator;

doesn't see well enough to me. Perhaps:

typedef list_type::iterator foo_iterator;
typedef list_type::const iterator const_foo_iterator;

is more appropriate.
There's also no conversion of pointers to iterators,
no addition of values to iterators ("list.begin()+x"),
no nothing!
Nope. Try:

std::vector<intv;
v.push_back(1);
v.push_back(2);
assert( *(v.begin()+1) == 2 );

Hope this help.

Dec 21 '06 #4

P: n/a

fungus wrote:
I'm moving some code from VC++ 6 to VC++ 2005 and
I've run into a nasty problem because iterators
are no longer pointers.

In the program I'm moving, there's a std::vector
of items hidden inside a class and users of the
class get to iterate it via a const_iterator.

eg.

class foo {
typedef std::vector<bar>list_type;
list_type list;
public:
typedef list_type::const iterator iterator;
iterator begin() { return list.begin(); }
iterator end() { return list.end(); }
};
Probably won't compile, it should be const_iterator not const iterator.
Assuming you fix that, there are still several inconsistencies here.

1. You call a vector list.
2. You call a const_iterator iterator.
3. Your methods begin() and end() return const_iterators but are
non-const methods,
although that is perhaps consistent with calling it iterator.

However if you can't do myIter->non_const_method() then it's a
const_iterator and should be labelled as such.
>
Then:

for (foo::iterator i=myFoo.begin(); i!=myFoo.end(); ++i) {
// ...
}
Usually the wrong way to iterate through a collection. Better to use
algorithms.
Ok, the problem:

Class foo defines a function "erase" to remove a
bar from the list, ie.:

class foo {
...
void erase(iterator i) {
}
};

How can I call std::vector::erase() if I only have
a const iterator? When iterators were pointers I
could just do a const_cast and it worked. With the
new style iterators there's no way to convert a
const iterator to non-const.
You can't, but if I am assuming that foo wraps vector in such a way to
make the contents unmodifiable whilst perhaps allowing the vector
itself to be modified (adding and removing elements from it) then your
best option is to use one of the suggested methods to get a valid
iterator out of the const_iterator that you have been passed.
There's also no conversion of pointers to iterators,
no addition of values to iterators ("list.begin()+x"),
no nothing!

I'm stumped....
There is std::advance() and std::distance()

Dec 21 '06 #5

P: n/a
flagos wrote:
Fungus:
>class foo {
typedef std::vector<bar>list_type;
list_type list;
public:
typedef list_type::const iterator iterator;
iterator begin() { return list.begin(); }
iterator end() { return list.end(); }
};

The line:

typedef list_type::const iterator iterator;

doesn't see well enough to me. Perhaps:

typedef list_type::iterator foo_iterator;
typedef list_type::const iterator const_foo_iterator;
is more appropriate.
Well...the users only ever get a const iterator.
The whole point of this is to prevent them from
modifying the list.
>There's also no conversion of pointers to iterators,
no addition of values to iterators ("list.begin()+x"),
no nothing!

Nope. Try:

std::vector<intv;
v.push_back(1);
v.push_back(2);
assert( *(v.begin()+1) == 2 );

Hope this help.
Yes, you're right, you must be able to add/subtract
values from iterators or it would break the standard.

I've figure out the problem, there was some other weird
stuff in the class which was stopping it from working.
--
<\___/>
/ O O \
\_____/ FTB. For email, remove my socks.
Weíre judging how a candidate will handle a nuclear
crisis by how well his staff creates campaign ads.
Itís a completely nonsensical process.
Dec 21 '06 #6

P: n/a

Rolf Magnus wrote:
Hmm, the standard library seems to be a bit inconsistent here with the core
language, where deleting an object doesn't count as modification. I wonder
why you can't erase with a const_iterator.
Because it modifies the iterator. Think about it. Often times erasing
an element invalidates all iterators. Not only that, but erasing the
element...erases the "const" object the iterator points at. Also, a
const_iterator is an iterator to a const container (remember, that's
where it came from) so erasing an element pointed to by that iterator
wouldn't exactly be const correct either.

Dec 21 '06 #7

P: n/a
Noah Roberts wrote:
>
Rolf Magnus wrote:
>Hmm, the standard library seems to be a bit inconsistent here with the
core language, where deleting an object doesn't count as modification. I
wonder why you can't erase with a const_iterator.

Because it modifies the iterator. Think about it. Often times erasing an
element invalidates all iterators.
So? There is a difference between a const iterator an const_iterator. You
can also modify a const_iterator using its operator++. It's not the
iterator that is constant, but the object it refers to.
Not only that, but erasing the element...erases the "const" object the
iterator points at.
What's the difference in this respect between:

void myfunc1(const foo* ptr)
{
delete ptr; // valid, since destroying is not counted as modification
}

and

void myfunc2(vector<foo>::const_iterator it)
{
myvec.erase(it); // not valid, since erase needs non-const
}
Also, a const_iterator is an iterator to a const container (remember,
that's where it came from) so erasing an element pointed to by that
iterator wouldn't exactly be const correct either.
Hmm, so you say the whole container is considered constant when using a
const_iterator? I was only thinking about the object that it refers to.

Dec 21 '06 #8

P: n/a

Noah Roberts skrev:
Rolf Magnus wrote:
Hmm, the standard library seems to be a bit inconsistent here with the core
language, where deleting an object doesn't count as modification. I wonder
why you can't erase with a const_iterator.

Because it modifies the iterator. Think about it. Often times erasing
an element invalidates all iterators. Not only that, but erasing the
element...erases the "const" object the iterator points at. Also, a
const_iterator is an iterator to a const container (remember, that's
where it came from) so erasing an element pointed to by that iterator
wouldn't exactly be const correct either.
I agree with Rolf. It is inconsistent that you can't erase from
const_iterator. Keep in mind that it is perfectly legal to destroy
constant objects:

{
std::string const s("Soon to be destroyed");
}

/Peter

Dec 21 '06 #9

P: n/a

fungus wrote:
I'm moving some code from VC++ 6 to VC++ 2005 and
I've run into a nasty problem because iterators
are no longer pointers.
Iterators have never been pointers, even when and if these are
implemented as pointers.
>
In the program I'm moving, there's a std::vector
of items hidden inside a class and users of the
class get to iterate it via a const_iterator.

eg.

class foo {
typedef std::vector<bar>list_type;
list_type list;
public:
typedef list_type::const iterator iterator;
iterator begin() { return list.begin(); }
iterator end() { return list.end(); }
};
Consider your situation as an oportunity, you can get rid of the
assumptions in your code.
I'ld suggest not calling a vector a list, initialize your members
(including the container).
Use correct constantness for parameters and member functions.
Template the class, iterators become dependant types and the class can
then be reused virtually anywhere with template specializations where
required.

#include <vector>

template< typename T >
class V {
std::vector< T vt;
public:
V(size_t sz = 0, const T& r_t = T())
: vt(sz, r_t) { }
V(const V& copy) : vt()
{
vt = copy.vt;
}
/* iteration */
typedef typename std::vector< T >::iterator iterator;
iterator begin() { return vt.begin(); }
iterator end() { return vt.end(); }
typedef typename std::vector< T >::const_iterator const_iterator;
const_iterator begin() const { return vt.begin(); } // const mem_fun
const_iterator end() const { return vt.end(); } // const mem_fun
/* member functions */
void push_back(const T& r_t)
{
vt.push_back(r_t);
}
// etc
};
>

Then:

for (foo::iterator i=myFoo.begin(); i!=myFoo.end(); ++i) {
// ...
}

Ok, the problem:

Class foo defines a function "erase" to remove a
bar from the list, ie.:

class foo {
...
void erase(iterator i) {
}
};

How can I call std::vector::erase() if I only have
a const iterator? When iterators were pointers I
could just do a const_cast and it worked. With the
new style iterators there's no way to convert a
const iterator to non-const.

There's also no conversion of pointers to iterators,
no addition of values to iterators ("list.begin()+x"),
no nothing!
see the std::advance algorithm.
>
I'm stumped....
--
<\___/>
/ O O \
\_____/ FTB. For email, remove my socks.
We're judging how a candidate will handle a nuclear
crisis by how well his staff creates campaign ads.
It's a completely nonsensical process.
Dec 21 '06 #10

P: n/a

Rolf Magnus wrote:
Noah Roberts wrote:

Rolf Magnus wrote:
Hmm, the standard library seems to be a bit inconsistent here with the
core language, where deleting an object doesn't count as modification. I
wonder why you can't erase with a const_iterator.
Because it modifies the iterator. Think about it. Often times erasing an
element invalidates all iterators.

So? There is a difference between a const iterator an const_iterator. You
can also modify a const_iterator using its operator++. It's not the
iterator that is constant, but the object it refers to.
Right. Now imagine what occurs when you "erase" an element from a
vector. It isn't the same as saying "delete const_ptr", and in fact a
deletion never occurs. An assignment actually occurs...probably more
than one actually. Same for a deque.

Dec 21 '06 #11

P: n/a
"peter koch" <pe***************@gmail.comwrote in message
news:11**********************@80g2000cwy.googlegro ups.com...
I agree with Rolf. It is inconsistent that you can't erase from
const_iterator.
You're right. The strongest argument is that if you have the container to
which the iterator refers, you can always convert a const_iterator to a
plain iterator, merely by visiting every element of the container and
checking whether your iterator refers to that element. Of course there are
faster ways if your container supports random-access iterators, but the
point is the same either way:

1) If you have the container (and it's not a const container), you can
convert a const iterator to a plain iterator, albeit sometimes slowly.

2) If you don't have the container, you can't use the iterator to erase
an element.

Therefore, the correct criterion for container erasure is whether you have a
non-const reference to the container, not whether you have a const iterator.

The standards committee actually considered this issue, but it came to their
attention too late in the process and they decided not to make the change.
Dec 21 '06 #12

P: n/a
In article
<Pg*********************@bgtnsc04-news.ops.worldnet.att.net>,
"Andrew Koenig" <ar*@acm.orgwrote:
"peter koch" <pe***************@gmail.comwrote in message
news:11**********************@80g2000cwy.googlegro ups.com...
I agree with Rolf. It is inconsistent that you can't erase from
const_iterator.

You're right. The strongest argument is that if you have the container to
which the iterator refers, you can always convert a const_iterator to a
plain iterator, merely by visiting every element of the container and
checking whether your iterator refers to that element. Of course there are
faster ways if your container supports random-access iterators, but the
point is the same either way:

1) If you have the container (and it's not a const container), you can
convert a const iterator to a plain iterator, albeit sometimes slowly.

2) If you don't have the container, you can't use the iterator to erase
an element.

Therefore, the correct criterion for container erasure is whether you have a
non-const reference to the container, not whether you have a const iterator.

The standards committee actually considered this issue, but it came to their
attention too late in the process and they decided not to make the change.
We're still thinking about it. :-)

Actually we've done it for the unordered containers (see N2135). The
intent is to do the same thing for the existing containers.

-Howard
Dec 21 '06 #13

P: n/a
Rolf Magnus wrote:
void myfunc1(const foo* ptr)
{
delete ptr; // valid, since destroying is not counted as modification
}
I've never quite understood why this is valid. It seems clearly wrong
to me. The only possible reason I can think of is because if you did
something like:
const int * a = new int ;

then you'd have no way to release the memory you allocated. The
solution, of course, being don't do that to begin with.

--
Alan Johnson
Dec 22 '06 #14

P: n/a
Alan Johnson wrote:
Rolf Magnus wrote:
>void myfunc1(const foo* ptr)
{
delete ptr; // valid, since destroying is not counted as modification
}

I've never quite understood why this is valid. It seems clearly wrong
to me. The only possible reason I can think of is because if you did
something like:
const int * a = new int ;

then you'd have no way to release the memory you allocated. The
solution, of course, being don't do that to begin with.
Well, what if you don't allocate the object dynamically?

void myfunc()
{
const int i = 3;
}

you would expect to compile that even though the object needs to be
destroyed when the function scope is left. If const objects couldn't be
destroyed, you couldn't create them in the first place.
Dec 22 '06 #15

P: n/a
Alan Johnson wrote:
Rolf Magnus wrote:
void myfunc1(const foo* ptr)
{
delete ptr; // valid, since destroying is not counted as modification
}

I've never quite understood why this is valid. It seems clearly wrong
to me. The only possible reason I can think of is because if you did
something like:
const int * a = new int ;

then you'd have no way to release the memory you allocated. The
solution, of course, being don't do that to begin with.
In C++, the concept of constness is more aptly expressed in terms of
observing - and not so much in terms of modifying - an object. Simply
put, an object defined as const is one whose value will never be
observed to change. So no two observed values of a const object can
ever disagree with each other.

Since the value of an object that has been deleted (or, likewise, the
value of an object that has not yet been created) is a value that
cannot be observed at all - then it logically follows that there is
nothing about creating or deleting a const object in C++ that violates
its constness guarantee. Or to put it another way: there is no chance
that an object's observed value will disagree with prior observations
of that value, if the object in question (along with its value) cannot
be observed at all because it no longer exists.

Greg

Dec 22 '06 #16

This discussion thread is closed

Replies have been disabled for this discussion.