Reputation: 3164
Assume I have a virtual base class and some derived concrete classes:
class Base { ... }
class DerivedA : public Base { ... }
class DerivedB : public Base { ... }
class DerivedC : public Base { ... }
And somewhere I have vectors of objects of each derived class:
std::vector<DerivedA> my_a;
std::vector<DerivedB> my_b;
std::vector<DerivedC> my_c;
Now, quite often I need to iterate over all elements in all three vectors and exercise the base class interface. I could write three for-loops, doing exactly the same in each. But obviously that's a far from optimal solution.
Is there a clever way to concatenate the vectors into a common container with base-class pointers/references, such that I need to iterate only once? Or any other idea how to solve this elegantly?
Upvotes: 16
Views: 1303
Reputation: 93304
There's no need for polymorphism in your current situation. You could simply use a variadic template + higher-order function to iterate over the vectors. Here's a C++17 solution using a fold expression:
template <typename F, typename... Vectors>
void for_all_vectors(F&& f, Vectors&&... vs)
{
(std::for_each(std::forward<Vectors>(vs).begin(),
std::forward<Vectors>(vs).end(),
f), ...);
}
Usage:
int main()
{
std::vector<A> my_a;
std::vector<B> my_b;
std::vector<C> my_c;
for_all_vectors([](const auto& x){ something(x); }, my_a, my_b, my_c);
}
In C++11/14 you can replace the fold expression with for_each_argument
:
template <typename TF, typename... Ts>
void for_each_argument(TF&& f, Ts&&... xs)
{
return (void)std::initializer_list<int>{
(f(std::forward<Ts>(xs)), 0)...};
}
template <typename F, typename... Vectors>
void for_all_vectors(F&& f, Vectors&&... vs)
{
for_each_argument([&f](auto&& v)
{
std::for_each(v.begin(), v.end(), f);
}, std::forward<Vectors>(vs)...);
}
I explain the idea behind this snippet and expand upon it in this CppCon 2015 talk: "for_each_argument
explained and expanded".
Upvotes: 16
Reputation: 35154
A simple solution would be to use a template function that iterates over the members of a vector and calls the respective function:
class Base {
public:
virtual int getX() = 0;
};
class Derived1 : public Base {
public:
int x1=1;
virtual int getX() { return x1; };
};
class Derived2 : public Base {
public:
int x2=2;
virtual int getX() { return x2; };
};
template<typename T>
void callPolymorphic(std::vector<T> &v) {
for (T& a : v) {
cout << a.getX() << " ";
}
}
int main() {
std::vector<Derived1> my_1(5);
std::vector<Derived2> my_2(5);
callPolymorphic(my_1);
callPolymorphic(my_2);
return 0;
}
Upvotes: 4
Reputation: 15334
I would just create a function template or a generic lambda and call it three times:
auto my_loop = [](auto& vec){
for (auto& base : vec) {
// do something with base...
}
};
my_loop(my_a);
my_loop(my_b);
my_loop(my_c);
Upvotes: 2
Reputation: 11940
On the other hand, you can manufacture your own homemade view adapter:
#include <iostream>
#include <vector>
#include <functional>
#include <iterator>
struct Base {
virtual int f() const = 0;
virtual ~Base() {}
};
struct D1: Base {
int f() const { return 42; }
};
struct D2: Base {
int f() const { return 314; }
};
template<typename T, typename... Left, typename... Right>
inline std::vector<T, Left...> operator+(std::vector<T, Left...> &&left, std::vector<T, Right...> &&right) {
std::vector<T, Left...> retVal(std::move(left));
using mi = std::move_iterator<typename std::vector<T, Right...>::iterator>;
retVal.insert(retVal.end(), mi(right.begin()), mi(right.end()));
return retVal;
}
int main() {
std::vector<D1> v1(3);
std::vector<D2> v2(4);
using View = std::vector<std::reference_wrapper<const Base>>;
View b(View(v1.cbegin(), v1.cend()) + View(v2.cbegin(), v2.cend()));
for(Base const &item: b) std::cout << item.f() << std::endl;
}
(Note that the underlying viewed containers can be any, vector
s are but for instance, but their element types should be conformant.)
Upvotes: 1
Reputation: 952
Just have a pointer to the base class. You can't have a vector of type base and put derived classes into it because they might not be the same size, same functions, ect.
So what I would do is create a vector or type base* and then you can concatenate the pointers of the derived class.
Probably would look something like:
vector<base*> v;
v.push_back(&derivedClassVariableA);
v.push_back(&derivedClassVariableB);
Then as long as the functions you are looking to use are virtual in the base and are defined in the derived, you should be good to go
Upvotes: 4