I have two classes, class Subclass
inherits from the class Superclass
:
class Superclass { /* … */ };
class Subclass : public Superclass { /* … */ };
Somewhere in the code, I also have a vector of Subclass *
. I need to use vectors, since I want to avoid another allocation of list structures and a vector is returned by some other function:
std::vector<Subclass *> &myVector = getMyVector();
And I also have a function, let's call it printSuperclasses
, that is taking an argument with type std::vector<Superclass *> &
. How can I pass the vector shown above to the function? Using static_cast
does not work and I would like to avoid using reinterpret_cast
.
void printSuperclasses(std::vector<Superclass *> const &);
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
// or another type?
If I understand it correctly, both vectors of Superclass *
and Subclass *
should be represented in the same way in memory (Are they?), so I should be able to convert from the later to the first. Is there a way to do it in C safely? Should the function receive it not as a vector, but as something different (iterators etc.)?
CodePudding user response:
You can't safely pass a std::vector<Subclass *>
to a function taking std::vector<Superclass *> &
.
What if it did the following?
class Other : public Superclass {};
void takesSuperclass(std::vector<Superclass *> & vec)
{
vec.push_back(new Other);
}
Now you may think to yourself, but my function doesn't do that. And you might be right, but you haven't told the type system that.
If you want to pass all kinds of ranges of pointers to takesSuperclass
, then you could make it a template
template<typename Supers>
auto takesSuperClass(Supers && supers)
In C 20, you can reasonably enforce those requirements
template<typename T>
using ptr_element_t = typename std::pointer_traits<T>::element_type;
template<typename Derived, typename Base>
concept derived_ptr = std::derived_from<ptr_element_t<Derived>, ptr_element_t<Base>>;
template<std::ranges::range Range>
requires derived_ptr<std::ranges::range_value_t<Range>, Super *>
auto takesSuperClass(Range supers)
If you don't want to have a template, you can also do some type-erasure, with e.g. boost::any_range
void takesSuperclass(boost::any_range<Super *, boost::forward_traversal_tag> supers)
CodePudding user response:
If you don't want to change the signature of the function printSuperclasses
, I would suggest the following. Write a wrapper function, which takes iterators as arguments, and then construct a vector inside that function, which will later be passed to the function you mentioned. This could be done like this
template<class Iter>
void printSuperclasses(Iter first, Iter last)
{
std::vector<Superclass*> superclass_vec(first, last);
printSuperclasses(superclass_vec);
}
The function will be able to convert the pointers if the Iter
is a iterator of a vector of pointers of any subclass of Superclass
.
The function would be called like this.
std::vector<Subclass*> subclass_vec; // not initialized, but it is not important in the case of example.
printSuperclasses(subclass_vec.begin(), subclass_vec.end());