Mine
Mine

Reputation: 4241

How to call inherited class's functions in templated class

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.

Upvotes: 1

Views: 108

Answers (3)

Jarod42
Jarod42

Reputation: 217283

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.

Upvotes: 1

Mine
Mine

Reputation: 4241

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

Upvotes: 2

Peter
Peter

Reputation: 36597

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.

Upvotes: 4

Related Questions