So my question is simple. I have std::vector<Derived>
and it’s a natural desire to have a std::span<Base>
to it. Derive
inherits from Base
:
#include <span>
#include <vector>
class Base {};
class Derived : public Base {};
int main
{
std::vector<Derived> vec {Derived{}, Derived{}};
// std::span<Base> span = ???;
return 0;
}
I guess this is impossible. Is there any common workaround? If so, are there any limitations of that?
I’m stuck with that. I see no solution
Before using C++20 <ranges>
, I like to do some boilerplate code:
#include <ranges>
#include <concepts>
#includr <type_traits>
template<typename to>
constexpr auto object_static_cast =
[]<std::convertible_to<to> from>(from&& src)
-> decltype(auto)
{ return static_cast<to>(std::forward<from>(src); };
template<typename to>
constexpr auto static_element_cast =
std::views::transform(object_static_cast);
Now assume that we have an algorithm for randomly accessible containers with available element count:
void my_algo(std::ranges::view auto rng)
requires std::ranges::random_access_range<decltype(rng)>
and std::ranges::sized_range<decltype(rng)>
and std::same_as<Base
, std::ranges:: range_reference_t<decltype(rng)>{
/* Whatever */
};
This algorithm can accept a std::span
which is a ranges::contiguous_range
; that means std::span
is more than qualified to be used as an input to my_algo
:
std::vector<Base> my_base_vec;
my_algo(span(my_base_vec));
my_algo(my_base_vec | std::views::all);
I deliberately forced users of my_algo
to explicitly convert the parameter to a ranges::view
instead of accepting a container object by reference. The explicitness of this conversion IMO increases readability. In order to call the algorithm on a range of derived objects I can perform a simple transformation:
std::vector<Derived> my_derived_vec;
my_algo(my_derived_vec | static_element_cast<Base&>);
Alternatively, I could change the last constraint on my_algo
to
std::derived_from
< std::ranges::range_value_t<decltype(rng)>
, Base >
. Thus, avoiding the need to transform the input:
my_algo(my_derived_vec | std::views::all);
my_algo(span(my_derived_vec));
Notice that for ranges::sized_range
classes, views::all
is practically equivalent to span
.
In general, references to objects of ranges::contiguous_range
classes cannot be converted by-reference to any other type. The elements-wise conversion however, can be fullfilled via a views::transform
which is used as the implementation of static_element_cast
in my first snippet. Then the algorithm needs to be generic.
Another option would be to use std::vector<smart_ptr<Base>>
as the desired container - with smart_ptr
being std::unique_ptr
or std::shared_ptr
, etc. That would avoid defining a generic algorithm in header, but at the cost of extra memory use and runtime overhead - due to extra runtime indirections leading to increased number of cach-miss.