I want a covariant wrapper for std::vector
. My idea was to do something like this:
BaseVector<B>
whose begin
and end
methods just forward to pure virtual functions. DerivedVector<B, D>
that wraps a std::vector<D>
. Its begin
and end
methods would shadow the base class's, and it would implement the virtual methods that the base class forwards to. In this way, if you have a pointer to a BaseVector
you can iterate over base class instances, and if you have a DerivedVector
pointer you can iterate over derived class instances.
( EDIT : Obviously the BaseVector
will necessarily not support insertion, since it can't know the type of the objects it contains. Perhaps this means "Vector" isn't the best name for it; I'm open to suggestions. Thanks @CygnusX1.)
For the DerivedVector
class the begin and end methods could just forward to std::vector
's.
Question: How could I implement the methods the BaseVector
begin
and end
forward to? Do I have to write my own iterator class that wraps the std::vector
iterators?
Alternatively: Is there a better or easier way to do this?
A DerivedVector<B, D>
instance needs to be usable through a BaseVector<B>
pointer in code that knows about B
but not D
, and it needs to have equivalent performance to just holding a derived class vector (if the calling code knew about the derived class)
Sample use case :
A library provides a BaseWidget
class and a BaseWidgetPool
class, which the user derives from. Due to the nature of the library, in any given program there will be exactly one DerivedWidget
class, but this class will differ for every program using the library.
The BaseWidgetPool
class includes methods that iterate over all the widgets in the pool and make use of their BaseWidget
functionality. But each program's DerivedWidgetPool
is likely to want to use the DerivedWidget
functionality of its contents.
(I know I could also handle this by making a generic WidgetPool<T>
class, but I'd rather encapsulate T
to the part of the code which actually uses it.)
With ranges, this become a non issue. It has all the api you need for polymorphic ranges:
#include <range/v3/all.hpp>
namespace rv = ranges::view;
int main() {
std::vector<Derived> derived_container;
// ... fill it
ranges::any_view<Base*> base_view =
rv::all(derived_container) | rv::transform([](auto& e) -> Base* { return &e; });
// do stuff with `base_view`
}
Then you can use base_view
as long as derived_container
lives.
I used any_view
so that the range is type erased. You can pass it around without needing to templatize function and you can pass it in virtual functions.
If you cannot use std ranges nor range v3, I'd create a simialar wrapper. So instead of a exposing a class herarchy, you hide it in the utility class.
Of course, the any_view
is there only to avoid templating function over the range type when passing it around. You can instead directly materialize the range into a vector:
auto base_view =
rv::all(derived_container) | rv::transform([](auto& e) -> Base* { return &e; });
std::vector<Base*> base_container = ranges::to<std::vector>(base_view);
This will avoid overhead during iteration but require a memory allocation for the new vector.
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.