Reputation: 4849
Consider this code:
#include <iostream>
class A {
public:
template <typename... Args> void execute(Args&&... args) {foo(std::forward<Args>(args)...);}
template <typename... Args> void doIt(Args&&... args) {bar(std::forward<Args>(args)...);}
private:
void foo() {}
void foo(int) {}
void foo(int, char) {}
void foo(bool, int, double) {}
void bar() {foo();} // *** Added
void bar(int) {}
void bar(int num, char c) {foo(num, c);} // *** Added
void bar(bool, int, double) {}
};
int main() {
A a;
a.doIt();
a.doIt(5,'t');
}
A::doIt
is to have the same overloading types as A::execute
and will use bar
's overloads for some of them (for which bar's overloads are unique) and use foo
's overloads for the others. To get this done, I simply added void bar() {foo();}
and void bar(int num, char c) {foo(num, c);}
. This is not that painful, but suppose there were many such forwarding to do. And think of the maintenance issues when new overloads of doIt
are needed, and new foo
overloads defined for them (easy to forget the forwarding stuff).
Is there a way to remove these extra bar
overloads (that simply forward to foo
), and instead modify the currently defined doIt(Args&&... args)
function so that in case no such bar overload exists it will call up foo
's overloads instead? So in other words, remove the two bar
overloads that I added, and still have main() compile as intended due to doIt
's new definition.
Upvotes: 0
Views: 105
Reputation: 4849
Thanks to nwp's hint (though I'm not sure if this is what he meant), and T.C.'s debugging hint, I have this alternate solution. But T.C.'s solution is way better.
#include <iostream>
class A_Foo {
protected:
virtual void foo() {std::cout << "foo().\n";}
virtual void foo(int) {std::cout << "foo(int).\n";}
virtual void foo(int, char) {std::cout << "foo(int, char).\n";}
virtual void foo(bool, int, double) {std::cout << "foo(bool, int, double).\n";}
};
class A_Bar : private A_Foo {
public:
using A_Foo::foo; // This is needed. Else all the declarations of foo in A_Foo are hidden.
virtual void foo(int) override {std::cout << "bar(int).\n";}
virtual void foo(bool, int, double) override {std::cout << "bar(bool, int, double).\n";}
};
class A : private A_Bar {
public:
template <typename... Args> void execute(Args&&... args) {
A_Foo::foo(std::forward<Args>(args)...);
}
template <typename... Args> void doIt(Args&&... args) {
A_Bar::foo(std::forward<Args>(args)...);
}
};
int main() {
A a;
a.doIt(); // foo().
a.doIt(5); // bar(int).
a.doIt(5,'t'); // foo(int, char).
a.doIt(true,5,1.8); // bar(bool, int, double).
}
Upvotes: 0
Reputation: 137301
SFINAE on the well-formedness of bar(std::forward<Args>(args)...)
.
class A {
private:
void foo();
void foo(int);
void foo(int, char);
void foo(bool, int, double);
void bar(int);
void bar(bool, int, double);
template <typename... Args>
auto doIt_impl(int, Args&&... args) -> decltype(bar(std::forward<Args>(args)...)){
bar(std::forward<Args>(args)...);
}
template <typename... Args>
auto doIt_impl(long, Args&&... args) -> void {
foo(std::forward<Args>(args)...);
}
public:
template <typename... Args> void doIt(Args&&... args) {
doIt_impl(0, std::forward<Args>(args)...);
}
};
The dummy first parameter ensures that the bar
-calling overload of doIt_impl
is preferred if viable. Also, it's important to note that trailing return types do not get class-scope lookup, so the declaration of doIt_impl
must be after the declaration of the bar
s.
Demo.
Upvotes: 4