How can I iterate over all base classes of a variadic template class and call a function for each of them.
Here is a minimal example:
struct A { void foo() { std::cout << "A" << std::endl; } };
struct B { void foo() { std::cout << "B" << std::endl; } };
struct C { void foo() { std::cout << "C" << std::endl; } };
template<typename... U>
struct X : public U...
{
void foo() {
static_cast<U*>(this)->foo()...; // ??? should call `foo` for all `U`
}
};
int main() {
X<A,B,C> x;
x.foo();
}
You can't normally without C++17's fold expressions. The ellipsis going there is not valid and the ellipsis going after the asterisk would create a list of pointer template arguments. For the appropriate pattern to be repeated, the ellipsis would have to be at the end of the statement, and that doesn't work here. I found this article to be a good resource for pack expansion.
Instead, there is a trick for it that doesn't require building up any recursive things:
int arr[] = {(static_cast<U*>(this)->foo(), 0)...};
This calls each function and then uses the result with the comma operator to produce the needed int
s. Unfortunately, this might result in an unused variable warning. One minimal way around this is to use a std::array
(or some class that can be initialized with an initializer list) and cast the result of creating an unnamed one of those to void
(casting to void
being a somewhat common technique for preventing the warning in general).
Here is a way:
struct thru{template<typename... A> thru(A&&...) {}};
struct A { void foo() { std::cout << "A" << std::endl; } };
struct B { void foo() { std::cout << "B" << std::endl; } };
struct C { void foo() { std::cout << "C" << std::endl; } };
template<typename... U>
struct X : public U...
{
void foo() { thru{(U::foo(), 0)...}; }
};
But if you care about the order of calls, watch out for the known gcc bug as discussed here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With