Jump to content
  • Advertisement
Sign in to follow this  
Lode

reinterpret_casting std::vector of pointers to other type

This topic is 3811 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Is the following code safe and can it work on any platform?
#include <iostream>
#include <vector>

struct A
{
  virtual int f() { return 5; }
};

struct B : public A
{
  virtual int f() { return 6; }
};

void g(std::vector<A*>& va)
{
  for(size_t i = 0; i < va.size(); i++)
  {
  std::cout << va->f() << " ";
  }
  std::cout << std::endl;
}

int main()
{
  std::vector<B*> vb;
  for(size_t i = 0; i < 10; i++) vb.push_back(new B);
  
  g(*reinterpret_cast<std::vector<A*>*>(&vb));

  for(size_t i = 0; i < 10; i++) delete vb;

  std::cin.get();
}


It outputs 6 6 6 6 6 6 6 6 6 6. The thing is: you can cast classes to their polymorphic types if you have 1 item. But if you have multiple items (in the form of a vector of them) that feature isn't available anymore, while it would be very very handy. With this reinterpret_cast it seems to be possible, but is it safe?

Share this post


Link to post
Share on other sites
Advertisement
No. The memory layout of an std::vector can depend on its type parameter or allocator, and as such attempting such a conversion can cause problems. Besides, such a cast allows you to violate invariants (for instance, casting a vector-of-derived to a vector-of-base, and pushing an instance-of-base in the vector, resulting in a vector-of-derived containing an instance-of-base).

Generally, this sort of thing is done using iterator sequences instead of vectors, because the only thing you should reasonably need in this situation is a const_iterator, and not the entire vector.

Share this post


Link to post
Share on other sites
Also, if you have an object and two pointers to that object one as a base class and one as a derived class, the bit patterns of those two pointers is not guaranteed to be the same.

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
Also, if you have an object and two pointers to that object one as a base class and one as a derived class, the bit patterns of those two pointers is not guaranteed to be the same.


Doesn't that only practically come up with multiple inheritance, though?

Share this post


Link to post
Share on other sites
Or virtual inheritance even if there is no multiple inheritance. Or if the base class lacks virtual member functions and the derived class has virtual member functions (of course in this case, you've got a lot of other problems to worry about).

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
Or virtual inheritance even if there is no multiple inheritance. Or if the base class lacks virtual member functions and the derived class has virtual member functions (of course in this case, you've got a lot of other problems to worry about).


Man, what a misery C++ can be once you want to do something special.

Share this post


Link to post
Share on other sites
Quote:
Original post by Lode
Quote:
Original post by SiCrane
Or virtual inheritance even if there is no multiple inheritance. Or if the base class lacks virtual member functions and the derived class has virtual member functions (of course in this case, you've got a lot of other problems to worry about).


Man, what a misery C++ can be once you want to do something special.


No no, it's a misery when you don't want to do something special too.

Which is part of the reason I haven't bothered fixing libindustry's virtual iterators to be able to write:

typedef industry::iterators::virtual_forward_iterator<A*> Aiterator;

void g( Aiterator begin, Aiterator end )
{
for ( Aiterator i = begin ; i != end ; ++i )
{
std::cout << (*i)->f() << " ";
}
std::cout << std::endl;
}


Of course, even simpler than this is to just make g a template function:

template < typename Iterator >
void g( Iterator begin, Iterator end )
{
for ( Iterator i = begin ; i != end ; ++i )
{
std::cout << (*i)->f() << " ";
}
std::cout << std::endl;
}

Share this post


Link to post
Share on other sites
But what if I'm writing a library? In such a case, templates can't really be used since you must expose source code. What would be the best option for working on ranges of values, in that case? (pointers and iterators won't work, since an iterator-of-Base is not an iterator-of-Derived, same for pointers).

Share this post


Link to post
Share on other sites
Quote:
Original post by Sc4Freak
But what if I'm writing a library? In such a case, templates can't really be used since you must expose source code. What would be the best option for working on ranges of values, in that case? (pointers and iterators won't work, since an iterator-of-Base is not an iterator-of-Derived, same for pointers).


Worst case scenario, make an interface and write an adaptor (possibly templated) from the SC++L containers to that interface. For me, the interface I would chose would take the form of a fixed iterator type, especially since I already have a class written (the aforementioned virtual iterators) which it would make sense to allow conversion for.

However, I'm on another C++ hating stint, and the fixes don't seem quite trivial, so I'm not going to touch it for now.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!