Topic: Converting vector<foo*>::iterator to vector<const foo*>::iterator?
Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1998/02/10 Raw View
Marc Sherman wrote:
>
> In article <6bm6nv$emq@marianna.psu.edu>,
> Oleg Zabluda <zabluda@math.psu.edu> wrote:
> >4. The Standard says nothing about the relationship between
> > Container<T*> and Container<const T*>, and probably never will.
> >
> >5. The standard says nothing about the relationship between
> > Container<T> and Container<U>, where T and U have any relationship
> > other then being identical.
>
> These are the key points. Everyone knows that a container of apples
> is _not_ a container of fruit, since you can put a pear into a container
> of fruit, but you cannot put a pear into a container of apples. However,
> a const container of apples _is_ a const container of fruit -- since you
> can't put anything into a const container of fruit, there's no reason
> to not give a container of apples to someone (or some function) which
> expects a const container of fruit. However, the standard does not
> support this.
>
> I'm not suggesting changes to the standard (at least not this century).
> What I am asking is if anyone knows of any way I could write my accessor
> to safely get a reference to a const Container<const T*> (or a const_iterator
> of that container type) which actually refers to / iterates a Container<T*>,
> which is the internal representation of my data, preferably without copying
> the data every time the client wants to iterate it, or keeping two copies
> of the data around.
>
> Currently, I'm using a reinterpret_cast to convert deque<T*> to
> const deque<const T*>&, and it works correctly on all my platforms
> of interest. I know that it's not portable, but if the only portable
> solution adds huge runtime or memory costs, I'm going to stick with
> the illegal version.
What about this (untested):
template<class iterator> class const_ptr_iterator
{
iterator iter;
public:
typedef iterator::value_type value_type;
const_ptr_iterator(iterator i): iter(i) {}
const value_type& operator*() const { return *iter; }
const value_type* operator->() const { return &*iter; }
const_ptr_iterator& operator++() { return ++iter; }
const_ptr_iterator operator++(int) { return iter++; }
};
This should be as efficient as using the iterator directly
(only inline access, no additional data, no virtual functions),
but restricts access to only pointers to const (although
internally there are still pointers to non-const).
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: "Tom McKearney" <no@spam.com>
Date: 1998/02/10 Raw View
Marc Sherman wrote in message <6bciha$ac4@shell.magma.ca>...
>I have a class which contains a vector<foo*>. Internally, (including
>subclasses), I need complete access to the vector - I have a protected
>getter that returns a reference to the vector. Externally, I need to
>iterate the vector without changing the pointers, but I need to preserve
>const-ness - when my object is const, I only want to be able to iterate
>const foo*'s. Is this possible?
>
>Currently, I have what feels like a horrible hack:
>
>class myclass:
>{
> ....
> public:
> vector<foo*>::const_iterator vectBegin()
> { return mVect.begin(); }
> vector<const foo*>::const_iterator vectBegin() const
> { return mVect.begin(); }
>};
This code is not legal. vector<foo*> and vector<const foo*> are different
data types. You will
get an 'unable to convert X to Y' type error in the second one.
I am not sure if I understand your question correctly, but here is my
interpretation:
1) You have a const object that contains a vector of foo pointers.
2) You want to get an iterator for this vector which doesn't allow you to
modify what
is being pointed to by the foo pointers.
I don't believe that this can be done. A vector<> has 2 begin() functions
(as far as I know). one which returns a const_iterator and another which
returns a regular iterator. Which one is called is determined by the
"constness" of your calling object/function.
Here's the problem:
when you have vector<int>, the const version of the begin() function will
give you an iterator which, when dereferenced, will return you a 'const int'
or 'int const' (i.e. a constant version of what it contains).
SO... when you have a vector<int*>, the const version of the begin()
function will give you an iterator which, when dereferenced, will return you
a 'int* const' (i.e. a constant version of what it contains). Since it
contains pointers to integers, it will return you a const pointer to an
integer (int* const), not a pointer to a const integer(const int* or int
const*)
Thus, the only thing you gain by using const here is that you can't modify
the foo pointer itself, but you would be able to modify the foo object to
which it points.
The only way that I can imagine doing this is the following:
class myclass
{
vector<foo*> myVect;
vector<const foo*> myConstVect;
public:
vector<foo*>::iterator vectBegin() // did you mean for your first one
to be const_iterator??
{ return myVect.begin(); }
vector<const foo*>::const_iterator vectBegin() const
{ return myConstVect.begin(); }
void AddFoo(foo *pFoo)
{
// put pointer in BOTH vectors
myVect.push_back(pFoo);
myConstVect.push_back(pFoo);
// might have to do this though (can't remember and my compiler
isn't installed right now)
// myConstVect.push_back(const_cast<const foo*>(pFoo));
}
// NOTE: you will have to write all the remove functions, etc so that
they are removed from each vector
// every time. This smacks me as something that actually requires a
design change so that all this hacking
// doesn't continue. Maybe you should rethink the situation that got
you into this mess in the first place.
};
Hope this helps.
Tom McKearney
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern/std-c++/faq.html ]
Author: msherman@magma.ca (Marc Sherman)
Date: 1998/02/11 Raw View
In article <34E0515E.8ECE30DD@physik.tu-muenchen.de>,
Christopher Eltschka <celtschk@physik.tu-muenchen.de> wrote:
>What about this (untested):
>
>template<class iterator> class const_ptr_iterator
>{
> iterator iter;
>public:
> typedef iterator::value_type value_type;
> const_ptr_iterator(iterator i): iter(i) {}
> const value_type& operator*() const { return *iter; }
> const value_type* operator->() const { return &*iter; }
> const_ptr_iterator& operator++() { return ++iter; }
> const_ptr_iterator operator++(int) { return iter++; }
>};
That's pretty much the same as a const_iterator -- value_type is a
pointer to T, and const value_type is a T*const, not the T const *
that I need. What I've been thinking of is more:
template <class T> class ptr_traits
{};
template <class T> class ptr_traits<T*>
{ typedef T value_type; }
template <class iterator> class const_ptr_iterator
{
iterator iter;
public:
typedef ptr_traits<iterator::value_type>::value_type const * value_type;
const_ptr_iterator(iterator i): iter(i) {}
const value_type& operator*() const { return *iter; }
const value_type* operator->() const { return &*iter; }
const_ptr_iterator& operator++() { ++iter; return *this; } // fixed
const_ptr_iterator operator++(int) { return iter++; }
// other operators and friend global ops as well...
};
But, my compiler does not implement partial specialization, so I can't
actually implement ptr_traits.
- Marc
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: phalpern@truffle.ma.ultranet.com (Pablo Halpern)
Date: 1998/02/12 Raw View
msherman@magma.ca (Marc Sherman) wrote:
>I have a class which contains a vector<foo*>. Internally, (including
>subclasses), I need complete access to the vector - I have a protected
>getter that returns a reference to the vector. Externally, I need to
>iterate the vector without changing the pointers, but I need to preserve
>const-ness - when my object is const, I only want to be able to iterate
>const foo*'s. Is this possible?
What you are asking for is safe in your specific case, but dangerous in
the general case. Hence it is not allowed. Here's why
vector<foo*>::iterator is similar to foo** (pointer to foo*)
vector<const foo*>::iterator
is similar to const foo** (pointer to const foo*)
Converting vector<foo*>::iterator to vector<const foo*>::iterator
is similar to converting foo** to const foo** which has been shown to be
dangerous:
const foo f;
vector<foo*> vf(1, NULL);
vector<const foo*>::iterator vfi = vf.begin(); // not allowed
*vfi = &f; // vf[0] now points to a const f
*vf[0] = foo(); // Modifies a const foo
However, converting foo** to const foo* const* is safe and legal. Thus,
it would be resonable to allow vector<foo*>::iterator to be converted to
a vector<const foo*>::const_iterator or a vector<foo*>::const_iterator
to be conveted to a vector<const foo*>::const_iterator. Unfortunately,
vector<foo*> and vector<const foo*> are unrelated in template-land.
It is probably safe and reasonably portable to use a reinterpret_cast<>
to do what you want. The following makes the (IMHO reasonable)
assumption that the structure of a vector<foo*> and a vector<const foo*>
are identical:
class myclass:
{
....
public:
vector<foo*>::const_iterator vectBegin()
{ return mVect.begin(); }
vector<const foo*>::const_iterator vectBegin() const
{ return reinterpret_cast<vector<const foo*>&>(mVect).begin(); }
};
-------------------------------------------------------------
Pablo Halpern phalpern@truffle.ultranet.com
I am self-employed. Therefore, my opinions *do* represent
those of my employer.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: msherman@magma.ca (Marc Sherman)
Date: 1998/02/08 Raw View
Sorry to follow up to my own post, but I've discovered some problems with
the code below that add another wrinkle to my previous post:
In article <6bciha$ac4@shell.magma.ca>, Marc Sherman <msherman@magma.ca> wrote:
>class myclass:
>{
> ....
> public:
> vector<foo*>::const_iterator vectBegin()
> { return mVect.begin(); }
> vector<const foo*>::const_iterator vectBegin() const
> { return mVect.begin(); }
>};
>
>Is this at all legal? Is there a legal way to get this result?
The code as written compiled fine, because my vendors stdlib defined
vector::iterator as a plain pointer, so the vector<Foo*>::const_iterator
was Foo*const, and the vector<const Foo*>::const_iterator was
const Foo*const -- the implicit conversion worked by accident. When
I changed the container type from vector to deque (which is actually more
appropriate to my algorithms), the code failed to compile, because
deque's iterator is a real class.
I thought that I might be able to implement a const_ptr_iterator wrapper
around the const_iterator returned by vectBegin() const, similar to the
common implementation of reverse_iterator, but since my compiler doesn't
support partial specialization yet, it would be tough to implement. Does
anyone have any ideas how to do this legally and portably?
Thanks,
- Marc
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: Oleg Zabluda <zabluda@math.psu.edu>
Date: 1998/02/09 Raw View
Marc Sherman <msherman@magma.ca> wrote:
: Sorry to follow up to my own post, but I've discovered some problems with
: the code below that add another wrinkle to my previous post:
: In article <6bciha$ac4@shell.magma.ca>, Marc Sherman <msherman@magma.ca> wrote:
: >class myclass:
: >{
: > ....
: > public:
: > vector<foo*>::const_iterator vectBegin()
: > { return mVect.begin(); }
: > vector<const foo*>::const_iterator vectBegin() const
: > { return mVect.begin(); }
: >};
: >
: >Is this at all legal? Is there a legal way to get this result?
: The code as written compiled fine, because my vendors stdlib defined
: vector::iterator as a plain pointer, so the vector<Foo*>::const_iterator
: was Foo*const, and the vector<const Foo*>::const_iterator was
: const Foo*const -- the implicit conversion worked by accident. When
: I changed the container type from vector to deque (which is actually more
: appropriate to my algorithms), the code failed to compile, because
: deque's iterator is a real class.
There was some discussions here lately about similar subjects.
Frankly, I have hard time understanding exactly what your problem
is. So, I'll write a very short summary of relevent info, and
if it does not answer your question, try exaplaining your problems to
me again.
1. The Standard mandates that there be a conversion from
Container<T>::iterator to Container<T>::const_iterator. This
was added only a couple of months ago.
2. The Standard does not mandate that there must _NOT_ be a conversion
from Container<T>::const_iterator to Container<T>::iterator.
I consider it to be a defect.
3. Part 1:
The Standard says nothing about the relationship between
Container<T> and Container<const T>, and from what I observe,
never will (formal language rules say that there is no
relationship whatsoever.)
Part 2:
I don't consider this to be a defect, because the way the Standard
is worded now, I see no point in creating Container<const T> at all,
since you can't in-place construct objects in there anyway. Everything
you can['t] do with Container<const T> you can['t] do with
const Container<T> either.
4. The Standard says nothing about the relationship between
Container<T*> and Container<const T*>, and probably never will.
5. The standard says nothing about the relationship between
Container<T> and Container<U>, where T and U have any relationship
other then being identical.
6. The Standard says that if iterator I is convertible to iterator J,
then reverse_iterator<I> must be convertible to reverse_iterator<J>.
That's as far as conversions go.
Oleg.
--
Life is a sexually transmitted, 100% lethal disease.
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: msherman@magma.ca (Marc Sherman)
Date: 1998/02/10 Raw View
In article <6bm6nv$emq@marianna.psu.edu>,
Oleg Zabluda <zabluda@math.psu.edu> wrote:
>4. The Standard says nothing about the relationship between
> Container<T*> and Container<const T*>, and probably never will.
>
>5. The standard says nothing about the relationship between
> Container<T> and Container<U>, where T and U have any relationship
> other then being identical.
These are the key points. Everyone knows that a container of apples
is _not_ a container of fruit, since you can put a pear into a container
of fruit, but you cannot put a pear into a container of apples. However,
a const container of apples _is_ a const container of fruit -- since you
can't put anything into a const container of fruit, there's no reason
to not give a container of apples to someone (or some function) which
expects a const container of fruit. However, the standard does not
support this.
I'm not suggesting changes to the standard (at least not this century).
What I am asking is if anyone knows of any way I could write my accessor
to safely get a reference to a const Container<const T*> (or a const_iterator
of that container type) which actually refers to / iterates a Container<T*>,
which is the internal representation of my data, preferably without copying
the data every time the client wants to iterate it, or keeping two copies
of the data around.
Currently, I'm using a reinterpret_cast to convert deque<T*> to
const deque<const T*>&, and it works correctly on all my platforms
of interest. I know that it's not portable, but if the only portable
solution adds huge runtime or memory costs, I'm going to stick with
the illegal version.
- Marc
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]
Author: msherman@magma.ca (Marc Sherman)
Date: 1998/02/06 Raw View
I have a class which contains a vector<foo*>. Internally, (including
subclasses), I need complete access to the vector - I have a protected
getter that returns a reference to the vector. Externally, I need to
iterate the vector without changing the pointers, but I need to preserve
const-ness - when my object is const, I only want to be able to iterate
const foo*'s. Is this possible?
Currently, I have what feels like a horrible hack:
class myclass:
{
....
public:
vector<foo*>::const_iterator vectBegin()
{ return mVect.begin(); }
vector<const foo*>::const_iterator vectBegin() const
{ return mVect.begin(); }
};
Is this at all legal? Is there a legal way to get this result?
Please cc replies by email, as my news access is spotty. Thanks in advance,
- Marc
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern/std-c++/faq.html ]