1

If a class inherits multiple classes with the same function, how does it call each inherited class's function without manually specific each class?

Example code as below:

#include <cstdio>

class Interface1
{
  public:
    virtual ~Interface1() = default;
    void foo()
    {
        printf("%s\n", __PRETTY_FUNCTION__);
    }
};

class Interface2
{
  public:
    virtual ~Interface2() = default;
    void foo()
    {
        printf("%s\n", __PRETTY_FUNCTION__);
    }
};

class ObjectWithoutTemplate : public Interface1, Interface2
{
  public:
    void foo()
    {
        // How do I write the code to call each InterfaceX's foo() here
        // without manually specify each class?
        Interface1::foo();
        Interface2::foo();
        // The desired code looke like
        // for each interface in Interfaces {
        //     interface::foo()
        // }
    }
};

template <class... Interfaces>
class ObjectWithTemplate : Interfaces...
{
  public:
    void foo()
    {
        // It does not compile if the template does not inherit Interface[1|2]
        Interface1::foo();
        Interface2::foo();
        // The desired code looke like
        // for each interface in Interfaces {
        //     interface::foo()
        // }
    }
};
int main()
{
    ObjectWithoutTemplate objWithout;
    ObjectWithTemplate<Interface1, Interface2> objWith;
    objWithout.foo();
    objWith.foo();
    return 0;
}

For ObjectWithoutTemplate, I could call interfaces' foo() by manually specifying the interface:

    Interface1::foo();
    Interface2::foo();

But for ObjectWithTemplate's foo(), how do I write the code to call each inherited interfaces' foo(), considering there will be Interface3, Interface4 as well.

Mine
  • 4,123
  • 1
  • 25
  • 46

3 Answers3

4

Assuming you don't want any of the bases to be repeated (and none of the bases inherit from each other) you can do this;

template <class FirstInterface, class... Interfaces>
class ObjectWithTemplate : public FirstInterface, public ObjectWithTemplate<Interfaces...>
{
   public:
      void foo()
      {
          FirstInterface::foo();
          ObjectWithTemplate<Interfaces...>::foo();
      };
};

// partial specialisation
template<class LastInterface> 
class ObjectWithTemplate<LastInterface> : public LastInterface
{
    public:
       void foo()
       {
           LastInterface::foo();
       };
};

An object of type

ObjectWithTemplate<Interface1, Interface2> object;

actually has Interface1 and ObjectWithTemplate<Interface2> as base classes. ObjectWithTemplate<Interface2>, in turn, has Interface2 as a base class.

If you repeat bases, or use two bases that share another base such as

ObjectWithTemplate<Interface1, Interface1> object;

ObjectWithTemplate<Interface1, SomethingDerivedFromInterface1> object2;

then the code will not compile due to ambiguity.

Peter
  • 35,646
  • 4
  • 32
  • 74
  • Yes, it works and compares to my own answer, it does not require extra space of `std::tuple`! Could you kindly check my answer (which is less code) and perhaps improves it to make it not using `std::tuple`? – Mine Sep 23 '19 at 07:04
  • Recursion can be avoided with folding expression. – Jarod42 Sep 23 '19 at 08:08
2

Inspired by Generating one class member per variadic template argument, using std::tuple<> makes it work.

It's not perfect because it increases the binary size, but it works cleanly.

template <class... Interfaces>
class ObjectWithTemplate : public Interfaces...
{
  public:
    std::tuple<Interfaces...> interfaces;

    void foo()
    {
        // The size becomes 8 * num-of-inherited-classes
        printf("sizeof(interfaces)=%zu\n", sizeof(interfaces));
        std::apply([&](auto&&... args) {
            (static_cast<decltype(args)>(*this).foo(), ...);
            },
            interfaces);
    }
};
Mine
  • 4,123
  • 1
  • 25
  • 46
1

Folding expressions from C++17 helps here:

template <class... Interfaces>
class ObjectWithTemplate : public Interfaces...
{
  public:
    void foo()
    {
        (Interfaces::foo(), ...);
    }
};

For C++11/C++14 version, it can also be done but is more verbose.

Jarod42
  • 203,559
  • 14
  • 181
  • 302