7

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?

1
  • 2
    int dummy[] = { (Args::doThings(), 0)... }; should do the trick Commented Apr 10, 2017 at 12:18

2 Answers 2

15

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()), ...);
    }
Sign up to request clarification or add additional context in comments.

2 Comments

This does work like a charm! I'm a little bit lost on how it works however -- could you provide an explanation/resource?
You may look for "iterate over a tuple". basically, ... can be used in some context, and we use one which guaranty order of evaluation. void() is to handle possible overload of comma operator. first 0 is to handle empty Args....
3

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

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

Live demo

4 Comments

As OP tag C++11, you should state that it is C++17.
@Jarod42 it's C++14 actually.
c++17 according to fold_expression (and clang gives me "warning: pack fold expression is a C++1z extension [-Wc++1z-extensions]").
@Jarod42 hmm looks like it's one of those things in n4296 that are not in the official 14 standard.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.