Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Concatenate containers of polymorphic objects

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?

like image 496
Georg P. Avatar asked Jul 06 '17 20:07

Georg P.


2 Answers

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);
}

live example on wandbox


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)...);
}

live example on wandbox

I explain the idea behind this snippet and expand upon it in this CppCon 2015 talk: "for_each_argument explained and expanded".

like image 79
Vittorio Romeo Avatar answered Sep 21 '22 18:09

Vittorio Romeo


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

like image 31
Chad K Avatar answered Sep 22 '22 18:09

Chad K