You need to unwrap the raw pointer out of the unique_ptr
first:
auto walkers = static_cast<Walker*>(ptr.get());
Note that the ownership of the pointer still belongs to ptr
, which is a std::unique_ptr<Person>
still. You can transfer the ownership like this:
std::unique_ptr<Walker> walkers(static_cast<Walker*>(ptr.release()));
If you want, you can write a function like this
template<typename Target, typename Source>
auto static_cast_unique(std::unique_ptr<Source> ptr) {
return std::unique_ptr<Target>(static_cast<Target*>(ptr.release()));
}
So you can write
auto walkers = static_cast_unique<Walker>(std::move(v1[1]));
Now, some issues with your code:
Person
needs a virtual destructor. You have Walker
objects owned by std::unique_ptr<Person>
s. That's OK in itself, but when those unique_ptr
s try to destroy their objects, they will call ~Person
, not ~Walker
(since that is what their type says to do), and that will be UB. Give Person
a virtual ~Person()
(and Walker
will inherit that virtual
ness)
class Person { public: virtual ~Person() = default; };
class Walker : public Person {};
std::unique_ptr(new ...)
is better written with std::make_unique
for (int i = 0; i < 4; i++) v1.push_back(std::make_unique<Walker>());
std::unique_ptr
s will automatically upcast.
In this case, it seems OK to use static_cast
, because you know that the Person*
you have actually points to a Walker
. However, you will want to use dynamic_cast
in the general case, so you can detect when the object isn't of the type you expect. I would use a function like this
template<typename Target, typename Source>
std::unique_ptr<Target> dynamic_cast_unique(std::unique_ptr<Source> &&ptr) {
if(auto ret = dynamic_cast<Target*>(ptr.get())) {
ptr.release();
return std::unique_ptr<Target>(ret);
} else return nullptr;
}
Which is used like
auto walkers = dynamic_cast_unique<Walker>(std::move(v1[1]));
If it succeeds, walkers
will own the object previously owned by v1[1]
and v1[1]
will be empty. Otherwise, walkers
will be empty and v1[1]
will be unchanged.