Topic: vector<base*> and vector<derived*>


Author: sbnaran@uiuc.edu (Siemel B. Naran)
Date: 1999/08/28
Raw View
On 26 Aug 1999 18:09:04 GMT, Siemel B. Naran <sbnaran@uiuc.edu> wrote:
>On 25 Aug 99 19:31:11 GMT, Martijn Lievaart <nobody@orion.nl> wrote:

>>conclusion that there is no guarentee that:
>>
>>vector<derived*>    vd;
>>vector<base*> *pvb = (vector<base*>*) &vd; // or reinterpret_cast
>>
>>will actually work. This is because although a) there is a guarenteed


>>1) Am I correct in the above reasoning?
>
>Yes, and for a very simple reason.  Consider this
>
>   Derived * array=new Derived[2];
>   Derived * d=array;
>   cout << (&d[1]-&d[0]) << '\n'; // prints sizeof(Derived)
>   Base * b=array;
>   cout << (&b[1]-&b[0]) << '\n'; // prints sizeof(Base)
>   delete[] array;

Oops.  I realized after posting that you have a vector<derived*> is
basically a derived**, so the above remarks don't appply.

While the conversion from derived** to base** is illegal, it will work
sometimes.  If the implementation stores derived class objects this
way: base part first, derived-only part next -- then &Base is equal to
&Derived.  In this case, the conversion from derived** to base** works.

Most likely, it won't work in the context of multiple inheritance and
virtual inheritance, but I don't know.  The rest of the post -- using
a pseudo-container or pseudo-iterator is still valid and might help.

--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------
---
[ 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: "Martijn Lievaart" <nobody@orion.nl>
Date: 1999/08/25
Raw View
Hello all,

I recently got thinking about converting containers of pointers to
derived objects to containers of pointers to base classes. I came to the
conclusion that there is no guarentee that:

vector<derived*>    vd;
vector<base*> *pvb = (vector<base*>*) &vd; // or reinterpret_cast

will actually work. This is because although a) there is a guarenteed
converion from derived to base and b) most implementations will not
change the bits the pointer is made of on leftmost derivation when doing
this conversion, there is no guarentee that this will always work.

The same goes the other way, when I *know* that all objects pointed to
are actually derived objects:

vector<base*>    vb;
vector<derived*> *pvd = (vector<derived*>*) &vb; // or reinterpret_cast

I have two questions regarding this.

1) Am I correct in the above reasoning?
2) If I am, are there any efficient solutions to achieve this effect?
All I can think of is creating a new vector, pre-size, copy.

This technique is actually used in the ObjectStore OODBMS (with their
own container classes), but that is so intertwined with the compiler
already that they can get away with it imo.

Thx in advance,
martijn
--
Please post replies to this newsgroup. If you must reach me by email,
use
<newsgroup-name> at greebo.orion in nl.
Senders of unsolicited bulk or commercial email will be prosecuted to
the
maximal extent possible by law and any other means.
---
[ 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: James Kuyper <kuyper@wizard.net>
Date: 1999/08/25
Raw View
Martijn Lievaart wrote:
>
> Hello all,
>
> I recently got thinking about converting containers of pointers to
> derived objects to containers of pointers to base classes. I came to the
> conclusion that there is no guarentee that:
>
> vector<derived*>    vd;
> vector<base*> *pvb = (vector<base*>*) &vd; // or reinterpret_cast
>
> will actually work. This is because although a) there is a guarenteed
> converion from derived to base and b) most implementations will not
> change the bits the pointer is made of on leftmost derivation when doing
> this conversion, there is no guarentee that this will always work.
>
> The same goes the other way, when I *know* that all objects pointed to
> are actually derived objects:
>
> vector<base*>    vb;
> vector<derived*> *pvd = (vector<derived*>*) &vb; // or reinterpret_cast
>
> I have two questions regarding this.
>
> 1) Am I correct in the above reasoning?

I think so. I certainly wouldn't use such code.

> 2) If I am, are there any efficient solutions to achieve this effect?
> All I can think of is creating a new vector, pre-size, copy.

Sounds right.

> This technique is actually used in the ObjectStore OODBMS (with their
> own container classes), but that is so intertwined with the compiler
> already that they can get away with it imo.

An additional issue, that may or may not be relevant, is whether a
specialization exists for vector<base*. or vector<derived*>. I've no
idea why anyone would want to, but there's probably some good reason,
and it would be legal. It's legal for the implementation to include one,
if 'derived' is a standard-defined or implementation-defined type. It's
legal for user code, if 'derived' is dependent on a user-defined type.

If such a specialization does exist, it probably isn't just a wrapper
for vector<void*>, like the typical vector<T*> partial specialization,
in which case all bets are off.


[ 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: sbnaran@uiuc.edu (Siemel B. Naran)
Date: 1999/08/26
Raw View
On 25 Aug 99 19:31:11 GMT, Martijn Lievaart <nobody@orion.nl> wrote:

>conclusion that there is no guarentee that:
>
>vector<derived*>    vd;
>vector<base*> *pvb = (vector<base*>*) &vd; // or reinterpret_cast
>
>will actually work. This is because although a) there is a guarenteed

>The same goes the other way, when I *know* that all objects pointed to
>are actually derived objects:
>
>vector<base*>    vb;
>vector<derived*> *pvd = (vector<derived*>*) &vb; // or reinterpret_cast


>1) Am I correct in the above reasoning?

Yes, and for a very simple reason.  Consider this

   Derived * array=new Derived[2];
   Derived * d=array;
   cout << (&d[1]-&d[0]) << '\n'; // prints sizeof(Derived)
   Base * b=array;
   cout << (&b[1]-&b[0]) << '\n'; // prints sizeof(Base)
   delete[] array;

IOW, b[1] is the 2nd element, assuming that every element is a Base
and occupies sizeof(Base) bytes.  But d[1] is the 2nd element, assuming
that every element is a Derived and occupies sizeof(Derived) bytes.

Note that std::vector<T> is just a wrapper for a T*.


>2) If I am, are there any efficient solutions to achieve this effect?
>All I can think of is creating a new vector, pre-size, copy.

You can create a pseudo-vector.

   // stores array of t
   // converts each t to T on demand
   template <class T, class t, class Alloc>
   class convertvector_t
   {
      typedef std::vector<t,Alloc> store_type;
      const store_type *const d_store;

      public:
         typedef typename store_type::size_type size_type;
         typedef T value_type; // note: not 't'
         convertvector_t(const store_type * store) : d_store(store) { }
         value_type operator[](size_type i) const
            { return static_cast<T>((*d_store)[i]); }
   };

   template <class T, class t, class Alloc>
   inline // ^^^^^^^ non-deduced template parameter first
   convertvector_t<T,t,Alloc>
   convertvector(const std::vector<t,Alloc> *);

We pass by pointer to prevent the client from passing a temporary:
   f(convertvector<Base*>( std::vector<Derived*>(3) ));

This is not that good because it only works with vector.
Write a class convert_iterator_t instead.


template <class Iter, class Manip>
  // Iter : any forward iterator
  // Manip: has operator() const that takes an Iter::value_type and returns a T
  // T may be some member of class Iter::value_type
  // or it may be some calculated field
class action_iterator_t
{
   typedef action_iterator_t self;

   public:
      typedef std::forward_iterator_tag        iterator_category;
      typedef typename Iter::difference_type   difference_type  ;
      typedef typename Iter::pointer           pointer          ;
      typedef typename Manip::result_type      result_type      ;

      action_iterator_t(Iter iter, Manip manip) : iter(iter), manip(manip) { }

      result_type operator*() const { return manip(*iter); }
      self& operator++() { ++iter; return *this; }
      const self operator++(int) { self old=*this; ++iter; return old; }

      bool operator==(const self& that) const { return this->iter==that.iter; }
      bool operator!=(const self& that) const { return this->iter!=that.iter; }

   private:
      Iter iter;
      const Manip manip;
};


template <class Iter, class Manip> inline
action_iterator_t<Iter,Manip>
action_iterator(Iter iter, Manip manip)
{
   return action_iterator_t<Iter,Manip>(iter,manip);
}

--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------


[ 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              ]