I'm going to list both solutions presented in other answers, and detail how they differ.
struct Child : Base { int foo(int x) { return Base::foo(x); } int foo(int, int, int) { std::cout << 3; return 33; } };
this does exactly what you want, but you have to repeat the signature.
A slightly different result is:
struct Child : Base { using Base::foo; int foo(int,int)=delete; int foo(int, int, int) { std::cout << 3; return 314; } };
To see how this is different, imagine we did this:
struct Grandkid : Child { using Child::foo; int foo(double, double) { std::cout << 2; return 42; } };
and we did:
Grandkid x; x.foo(3,4);
In the case with =delete, this would generate a compiler error.
3,4 prefers int,int over double,double. When we =delete the int,int overload, it is still considered, selected, and we pick the =deleted one. When we instead exclude it from overload resolution, 3,4 picks double,double.
Manual forwarding excludes int,int overload from being considered. =delete does not.
This is most similar to the imaginary syntax of using Base::foo(int); (ie, only bringing in one of the parent overloads of foo).
Note that Grandkid is just one simple way to detect the difference. The important thing is there is a difference between removing something from overload resolution, and =deleteing the overload.
Live example where it works, and where it doesn't.
Base* bPtr = someCondition? new Base(): new Child(); bPtr->foo(3);That perfecly valid, and it's going to callChild::foo(int), which you don't want. The question is: why do you want to use inheritance (is-a relation) and act as if Child is-not-a Base?Base::foo(int, int)on a derived instance?