简体   繁体   中英

is this code safe? cast a std::vector<Derived*> to std::vector<Base*>

Basically I've a class A and a class B : public A .

And I'd like to cast a std::shared_ptr<std::vector<A*> to a std::shared_ptr<std::vector<B*>

The problem is std::vector<B> doesn't inherit from std::vector<A> , and the smart_ptr neither. So I do a horrible cast :

  std::shared_ptr<VectorA> vector_a = * ((std::shared_ptr<VectorA>*)&vector_b);

The code compiles and runs there, but it is safe? http://liveworkspace.org/code/3dQTz1$0

This is a horrible solution. When you cast a vector of objects of one type to the other you can expect all kind of incorrect/undefined behaviour.

You should use vector of std::shared_ptr<A> from the beginning and initialize them with pointers to objects of type B there.

If it is not possible you can create a new vector holding std::weak_ptr<A> to avoid managing these objects twice.

Don't do that.

Consider that a vector of (pointers to) B is not a vector of (pointers to) A , or to be more precise, is not a universally valid substitution for a vector of (pointers to) A ; thus, there is a good reason why you cannot perform such a conversion.

Although it is true that all you have in a vector of B is indeed a set of objects which are (also( instances of A , consider the following algorithm:

void f(vector<A>& v)
{
    A a;
    v.push_back(a);
}

Now imagine you invoke f() with a vector of B . That would be an attempt to add an instance of a class that is not B to a collection which is supposed to contain only elements of type B .

The solution here is to make the code which accepts only a vector<A> flexible enough to work also on a vector<B> . In other words, you need to make it a template. For instance, your template could accept as arguments only vectors of a type which is derived from A . This is quite easy to enforce with some SFINAE techniques and type traits such as std::is_base_of .

Strictly speaking the dereferencing operations should succeed. Both vectors are pointer containers, so casting one to another, whilst unacceptable for production code, will still use the same dimensions and alignment.

C++ provides rich abstractions to avoid these shenanigans though, it would be better to populate the vector of derived objects as pointers to the base class.

You can still cast the elements:

A* a = vector_b->at (42);

gives you what you want.

Now, if you have written functions taking shared_ptr<vector<A*>> as arguments, then you have multiple solutions to take you out of this situation:

  • use shared_ptr<A*[]> instead of shared_ptr<vector<A*>> (it works as you'd like to)
  • take A** as arguments (and use vector_b->data () ) : it does not ties you to shared pointers.
  • Better: take iterators which dereference as [smart] pointers to A (recall that operator-> chains)
  • use std::vector<std::shared_ptr<A>> (wastes resources)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM