Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a less blunt way of bring base class methods into a child class?

Tags:

c++

c++11

Consider

struct Base
{
    int foo(int);
    int foo(int, int);
};

struct Child : Base
{
    using Base::foo;
    int foo(int, int, int);
};

Ideally I want to bring the Base class foo that takes only one int as a parameter into the Child class, and not the one that takes 2 ints. Is there a way I can do that? My writing using Base::foo; brings in both foo methods of Base.

like image 697
Sebastian John Howard Avatar asked Oct 03 '17 13:10

Sebastian John Howard


3 Answers

A using declaration will make all overloads available. You can't prevent that from happening. But you can delete the overloads you don't want, after the fact:

struct Base
{
    int foo(int);
    int foo(int, int);
};

struct Child : Base
{
    using Base::foo;
    int foo(int, int) = delete;
    int foo(int, int, int);
};

Now using the two int overloads with a Child is ill-formed. However that's not a perfect solution, since a client can still call the Base version:

Child c;
c.Base::foo(0, 0);
like image 69
StoryTeller - Unslander Monica Avatar answered Nov 08 '22 02:11

StoryTeller - Unslander Monica


It's not possible to bring the specified overloaded function into the derived class. The rule of name lookup deals with names, but the overloaded functions have the same name foo.

You can write a wrapper function at the derived class, e.g.

struct Child : Base
{
    int foo(int x) { return Base::foo(x); }
    int foo(int, int, int);
};
like image 35
songyuanyao Avatar answered Nov 08 '22 03:11

songyuanyao


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.

like image 42
Yakk - Adam Nevraumont Avatar answered Nov 08 '22 03:11

Yakk - Adam Nevraumont