2

I have a class Base:

class Base
{
};

and a derived class Derived:

class Derived: public Base
{
};

Now I need to get a vector of Derived Pointers:

std::vector<std::shared_ptr<Derived>> derivedVec;

but they are stored unfortunately as a vector of Base Pointers, e.g. I can access only the downcasted pointers:

std::vector<std::shared_ptr<Base>> baseVec;

How to I perform the conversion from std::vector<std::shared_ptr<Base>> to std::vector<std::shared_ptr<Derived>>. The solution is desired to be C++20 style.

Jan Hackenberg
  • 481
  • 3
  • 14
  • If `Base` is polymorphic (i.e. has at least one virtual member function), you may not need to downcast. And if you need to anyway, then you didn't want a vector of base in the first place. – Fareanor Aug 18 '23 at 13:35
  • 1
    The linked question doesn't actually contain the answer. This question is about the possibility of casting the whole vector. Using `boost` is not a valid answer, since this question does not contain `boost` tag – Sergey Kolesnik Aug 18 '23 at 13:36
  • 3
    [`views::transform`](https://godbolt.org/z/68rhsGG6f)? – 康桓瑋 Aug 18 '23 at 13:38
  • I'm reopening because it seems like the duplicate is incomplete. It explains how to cast a single pointer, but this question has an additional challenge of how to do that to a whole range. – François Andrieux Aug 18 '23 at 13:39
  • @康桓瑋 I haven't used views yet, but doesn't it require the underlying container to continue existing in order to be used? – François Andrieux Aug 18 '23 at 13:42
  • 1
    @FrançoisAndrieux Yes, my point of view is that there is no need to create additional vectors, just apply specific views on it. – 康桓瑋 Aug 18 '23 at 13:44
  • 1
    Did you try `std::vector> derivedVec{baseVec.begin(), baseVec.end()};`? – Sam Varshavchik Aug 18 '23 at 13:52
  • 2
    A few questions .... Is `Base` polymorphic? [As represented in your code as shown, it is NOT]. Do you *know* that all the elements of `baseVec` contain elements that point to a `Derived`? If not, what do you require/expect to happen if any of the elements of `baseVec` actually points to an `AnotherDerived` where, other than being both derived from `Base`, there is no relationship between `Derived` and `AnotherDerived`? – Peter Aug 18 '23 at 13:58

2 Answers2

0

Firstly, two vectors std::vector<A> and std::vector<B> aren't covariant, meaning that it's not enough to reinterpret one as the other. To get a std::vector<std::shared_ptr<Derived>>, you need to convert the vector:

#include <vector>
#include <memory>
#include <ranges>

// ...

std::vector<std::shared_ptr<Base>> base;
auto derived_view = base | std::views::transform([](const std::shared_ptr<Base> &b) {
    // or use std::dynamic_pointer_cast for polymorphic classes
    return std::static_pointer_cast<Derived>(b);
});

// If you don't need to create a new vector, but just want to examine the old one
// with the conversion applied, you can also skip this step and work with the view.
std::vector<std::shared_ptr<Derived>> derived(derived_view.begin(), derived_view.end());

// In C++23, you can also write:
std::vector<std::shared_ptr<Derived>> derived(std::from_range, derived_view);
// however, not every standard library supports this yet, at the time of writing

See live example at Compiler Explorer

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
0

This was my answer for integer conversion. For shared_ptr you can use std pointer casts instead:

#incluce <ranges>
template<typename result>
constexpr auto ptr_trans = std::views::transform( // C++20
            [](auto const& x)
            { return std::static_pointer_cast<result>(x); });

std::vector<std::shared_ptr<Base>> vbase;//original vector

auto r_drvd = v_base | ptr_trans<Derived>; //C++20: no copy

auto v_drvd = r_drvd | std::ranges::to<std::vector>; // C++23: create a copy
Red.Wave
  • 2,790
  • 11
  • 17