Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple inheritance with variadic templates: how to call function for each base class?

I have a diamond inheritance scheme, where the last child should be able to inherit from many different parents.

     A
    /|\
   / | \
  B  C  ...
  |  |  |
    * *
    D E

Now imagine I have a class D : public B, class E : public B, public C, etc. From D I want to call the same function of all its parents, which I am guaranteed exists due to the inheritance. My thought was that I could wrap this in some variadic template.

Currently I have this:

template <typename T>
class A
{
public:
    A(T t) : mT(t) {}
    virtual ~A() {}
    virtual void doThings() = 0;
protected:
    T mT;
};

template <typename T, typename A = A<T>>
class B : public A
{
public:
    B(T t) : A(t) {}
    virtual ~B() {}
    virtual void doThings() { std::cout << "B" << std::endl; }
};

template <typename T, typename A = A<T>>
class C : public A
{
public:
    C(T t) : A(t) {}
    virtual ~C() {}
    virtual void doThings() { std::cout << "C" << std::endl; }
};

Now I thought I could do something like this, which obviously does not work:

template <typename T, typename ...Args>
class ChildGenerator : public Args...
{
public:
    ChildGenerator(T t) : Args(t)... {}

    // The unpacking of the variadic template does not work here.
    // Do I need to make it recursive somehow? How can I do that without having to instantiate new classes B and C?
    void doThings() override { Args...::doThings();}
};

My hope is that I can use it like so:

int main()
{
    using B = B<double>;
    using C = C<double>;
    B c1(0.0);
    C c2(1.0);
    ChildGenerator<double, B, C> c3(2.0);
    c1.doThings();
    c2.doThings();
    c3.doThings();
 }

Expected output (order does not matter):

B
C
B // <-- order of these two does not matter
C // <--

Is what I'm trying to achieve possible?

like image 233
pingul Avatar asked Apr 10 '17 12:04

pingul


2 Answers

One way to iterate over the variadic bases:

template <typename T, typename ...Args>
class ChildGenerator : public Args...
{
public:
    ChildGenerator(T t) : Args(t)... {}

    void doThings() override {
        int dummy[] = {0, (Args::doThings(), void(), 0)...};
        static_cast<void>(dummy); // avoid warning for unused variable
    }
};

or in C++17, with folding expression:

    void doThings() override {
        (static_cast<void>(Args::doThings()), ...);
    }
like image 150
Jarod42 Avatar answered Sep 30 '22 04:09

Jarod42


Use a fold-expression (C++17):

void doThings() override { ((Args::doThings()) , ...);}

Live demo

like image 33
n. 1.8e9-where's-my-share m. Avatar answered Sep 30 '22 06:09

n. 1.8e9-where's-my-share m.