Reputation: 4241
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
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
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
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