What does this have to do with function overloading?

This is basically a copy from the example in Item 21. Overriding Virtual Functions in Herb Sutter's book Exceptional C++ .

 #include <iostream> #include <complex> using namespace std; class Base { public: virtual void f(int); virtual void f(double); virtual ~Base() {}; }; void Base::f(int) { cout << "Base::f(int)" << endl; } void Base::f( double ) { cout << "Base::f(double)" << endl; } class Derived: public Base { public: void f(complex<double>); }; void Derived::f(complex<double>) { cout << "Derived::f(complex)" << endl; } int main() { Base* pb = new Derived; pb->f(1.0); delete pb; } 

The code prints Base::f(double) and I have no problem with this. But I could not understand the explanation given by the author at the beginning of page 122 (my emphasis):

Interestingly, although Base * pb points to a Derived object, it calls Base :: f (double), because overload resolution is done on the static type (here Base), and not on the dynamic type (here Derived) .

I understand that calling pb->f(1.0) is a virtual call, and Base::f(double) is the final override for f(double) in Derived . What does this have to do with function overloading?

+7
c ++ override overloading c ++ 11
source share
3 answers

The delicate part here is that virtual methods are a mechanism for sending a function call, and overloading is a function that affects call resolution.

That is, for any call, the compiler needs to figure out which method should be called (enable it); subsequently and in a logically different operation, it needs to generate code that calls the correct implementation of this method (send it).

From the above definitions of Base and Derived you can easily justify that if f(double) is called on Base* , then the call should be sent to any derived redefinition (if applicable) in the preference of the base implementation. But the answer to this question answers in a completely different way than

When the source tells pb->f(1.0) which of the methods named f should be used to resolve the method call?

As Sutter explains, the specification says that when the call is resolved, the compiler will look for methods named f declared on a static type pointed to by pb ; in this case, the static type is Base* , so the overloads (not overrides!) declared on Derived are not considered at all. However, if the method allowing the call is virtual , then the possible implementation provided by Derived will be used as expected.

+12
source share

The reason this example is interesting is because if pb was Derived* instead of Base* , or if the compiler somehow used a dynamic type rather than a static type to perform overload resolution, the call coincides with pb->f(1.0) to void Derived::f(complex<double>) ( complex<double> can be implicitly built from double ). This is because having a function with the name f in the derived class effectively hides any overloads of the base class with the same name, even if their argument lists are different. But since the static type of pb is actually Base* , this does not happen.

+3
source share

In this example, despite the reappearance of virtual , there is no redefinition method at all; the f method in the derived class does not override any of the base class, because the argument types do not match. Given this circumstance, the pb->f method can never invoke the (unique) Derived::f method. Overload resolution / name lookup (which only considers methods of the static type pb->f ) must choose between the two methods declared as Base::f , and in this example it will select the one that has the type of argument double . (At run time, this can lead to an override if it is defined in a different derived class than Derived , and if the example is modified so that pb can point to an object of such another derived class.)

A separate problem is that methods named f in Base and Derived will not be considered at the same time to allow overloading if f is called from an expression (static) of type Derived either, because the methods in the base class are hidden by the declaration of f in Derived , so they not available for such calls. I think this hiding can be avoided by declaring using Base::f; , which will "raise" the Base::f methods in Derived , as if they were also declared there (but I admit that I don’t know the details of this mechanism; I believe that lifts will be an override of the virtual base method with the same type of argument, but that doesn't really matter, because elevators refer to an implementation in the base class.)

0
source share

All Articles