9

With Base and Derived defined like this:

class Base {

    public:
        virtual int f1(int a) const = 0;
        virtual int f2(int a, int b) const { return a+b;}
};

class Derived : public Base {

    public:
        int f1(int a) const { return a; }
}; 

int main() {
    Derived obj;
    cout << obj.f1(1) << endl;
    cout << obj.f2(1, 2) << endl;
}

The result is

1
3

obj.f1(1) uses the f1 implementation from Derived and obj.f2(1, 2) uses the implementation inherited from Base, which is what I want.

Now, I would like these two functions to have the same name, f, so the base class provides an implementation when there are two parameters and the derived class must implement the single parameter version (that's why it's pure virtual).

However, if I do this (just rename f1 and f2 to f):

class Base {

    public:
        virtual int f(int a) const = 0;
        virtual int f(int a, int b) const { return a + b;}
};

class Derived : public Base {

    public:
        int f(int a) const { return a; }
};

int main() {
    Derived obj;
    cout << obj.f(1) << endl;
    cout << obj.f(1, 2) << endl;
}

I get the following error:

20:23: error: no matching function for call to 'Derived::f(int, int)'
20:23: note: candidate is:
14:13: note: virtual int Derived::f(int) const
14:13: note:   candidate expects 1 argument, 2 provided

Why is this? Is it not possible to do this kind of overloading?

Milo
  • 2,062
  • 16
  • 38
  • @StoryTeller It is, indeed. When I did the research I thought it had something to do with one of the functions being pure virtual, so I guess that's why I didn't find that question. This answer: https://stackoverflow.com/a/1629074/1755482 gives a nice explanation of the *name hiding* phenomenon. – Milo Sep 17 '18 at 10:58

5 Answers5

10

You need to write

class Derived : public Base {

    public:
        using Base::f;
        int f(int a) const { return a; }
};

Note the using statement. That brings the base class version back into scope.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
8

Now, I would like these two functions to have the same name, f

You need to write

class Derived : public Base {

    public:
        using Base::f;
        int f(int a) const { return a; }
};

Note the using statement. That brings the base class version back into scope. [Thanks to @Bathsheba]

Why is this? Is it not possible to do this kind of overloading?

No, not possible as written in the original question due to [basic.scope.hiding¶3]:

In a member function definition, the declaration of a name at block scope hides the declaration of a member of the class with the same name; see [basic.scope.class]. The declaration of a member in a derived class hides the declaration of a member of a base class of the same name; see [class.member.lookup].

This clause is about names, not overloads. As such, it doesn't matter if there are other overloads in the base class, they all share the same name, which is hidden according to quote above.

Geezer
  • 5,600
  • 18
  • 31
1

You can either pull all definitions of Base into scope with by writing using Base::f; or you can explcitily write for some versions of f something like this: int f(int a, int b) const override {return Base::f(a,b);}

class Derived : public Base {

public:
    int f(int a) const { return a; }
    int f(int a, int b) const override {return Base::f(a,b);} 
};

The version with using is already mentioned in this answer:

class Derived : public Base {

    public:
        using Base::f;
        int f(int a) const { return a; }
};

Note: I know that the second version does not work on MSVC 2010. I am also aware this compiler is ancient, but just for people who care ;)

0

In addition to the other solutions, if you don't want to redefine f you could explicitly invoke f of the base class using

int main() {
    Derived obj;
    cout << obj.f1(1) << endl;
    cout << obj.Base::f2(1, 2) << endl;
}
dan
  • 303
  • 1
  • 3
-2

You can also make them all pure and then override them in the derived class. And you can continue to use your class as you want.

  • The problem with this answer is that you're inflicting extra work on the descendent to actually implement f2(). The original f2() is not pure virtual presumably because the base implementation is sufficient for many (most?) of their use-cases, but wanted to leave it open for descendants to override it should it become necessary. – Andre Kostur Sep 17 '18 at 14:00
  • @AndreKostur, your are completely right on this point. – Ibrahima Keita Sep 17 '18 at 17:29