Overriding of overloaded virtual member

I've got the following code with output. I can't figure out myself why it's what printed out there. I believe, it has something to deal with overloading/overriding/virtual functions implementations in C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Base{
   public: virtual void f(int);
           virtual void f(double); 
}
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(int);
}
void Derived::f(int) {cout << "Derived::f(int)" << endl;}

int main(void){
  Base b;
  Derived d;
  Base* pb = new Derived;

  b.f(1.0);
  d.f(1.0);
  pb->f(1.0);
  delete pb;
}


Here's the output:

Base::f(double);  // this is simple
Derived::f(int);  // why it's not 'Base::f(double)'
Base::f(double);  // let's accept the previous answer. Then: why it's not 'Derived::f(int)'


After debugging in MS VS 2010 I've found, that:
b is object with vfPtr pointing to vfTable, consisting from 2 virtual functions:
Base::f(double)
Base::f(int)

d is object with vfPtr pointing to vfTable, consisting from 2 virtual functions:
Base::f(double) // this wasn't overrided
Derived::f(int) // this is overriding function

Thus here're my conclusions:
1) in line
d.f(1.0);
for some reason compiler preferred casting double->int of the argument and then call to 'Derived::f(int)'.

2)in line
pb->f(1.0);
for some reason compiler preferred call to 'Base::f(double);'. 'Base' is static type of pb, but the dynamic type is 'Derived'.

I believe the answer has to deal with the fact whether virtual table contains in addition to functions' names also the types of arguments they accept. AFAIK, vTable doesn't include such info.

Any help would be greatly appreciated.
2. No, Derived does not provide a definition for f(double) so it does not override the version in Base.

As for 1, does the output change if you mark f virtual on line 9?

Edit: It is because of scope resolution. Derived::f is more in scope than Base::f is, so it chooses that one. If you put "using Base::f;" inside of Derived, it is in scope for the compiler to see it and use proper overload resolution.

http://ideone.com/nLKfbh

Kind of annoying, in my opinion, but the fix is simple enough. Now I'll have to ask why it silently casts the double to an int...
Last edited on
I would go for Name Hiding as the reason the for 1.
data hiding (is a compile time thing) - where a member in the derived class hides (ALL) the members in the base class that have the same name.

So as far as the compiler is concerned Derived only has one member function called f - and that is f(int).

From C++ document (pre-release)
3.3.10 Name hiding
A name can be hidden by an explicit declaration of that same name in a nested declarative region, more refined concept (14.9.3), or derived class (10.2).


so
1
2
  Derived d;
  d.f(1.0); //will only find Derived:: f(int) 
Last edited on
I looked up some stuff about implicit casts of double literals to ints, and the only ways I found to prevent it was either a template+type traits solution (not applicable here) and one using the C++11 =delete (also not applicable here). SO for this case, "using Base::f;" is the only solution.
Well, thanks you guys. If I understood you right - here're the correct answers:

1. By declaring function in Derived class with the same name as in Base class - I hide each and every function having the same name, despite possible differences in signatures for overloading functions. Thus, calling function 'f' on derived object will ''match'' the only function 'f', which is declared in Derived class and since its argument is 'int' and not 'double' - casting will be executed.

2. Since static type of 'pb' is Base - it's checked at compile time for having f(double) and finds Base::f(double). Then, at run time, since the dynamic type of 'pb' is Derived - the 'vfptr' is checked and in 'vtable' of 'Derived' 2 functions are found:
Base::f(double) // this wasn't overrided
Derived::f(int) // this is overriding function
and the more suitable one is chosen (Base::f(double))

Please let me know if I got it right or I have some misconception left behind. Thanks

BTW: I didn't need some specific output. I just wanted to understood why THIS output is correct. Anyway, thanks
Last edited on
It doesn't choose which overload to call at runtime, it does that at compile time. It chooses which overridden version at runtime, but it knows which overload at compile time.
> By declaring function in Derived class with the same name as in Base class - I hide each and every function having the same name

Yes. What is hidden is the name (which may be the name of a function)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
struct base
{
    virtual void foo(int) ;
    int bar ;
};

struct derived : base
{
    int foo ;
    virtual void bar(int) ;
};

struct more_derived : derived
{
    virtual void foo(int) ;
    virtual void bar(int) ;
};

int main()
{
    more_derived md ;
    derived* p_derived = &md ;
    base* p_base = p_derived ;

    p_base->foo(23) ; // fine, look up base::foo (compile time),
                      // call more_derived::foo(int) (run time)

    p_derived->foo(23) ; // look up derived::foo (compile time),
                         // *** error: derived::foo is not callable

    p_base->bar(23) ; // look up base::bar (compile time),
                      // *** error: base::bar is not callable

    p_derived->bar(23) ; // fine, look up derived::bar (compile time),
                         // call more_derived::bar(int) (run time)
}


'Hiding by name' is what C++ always does; other languages may differ.

For instance, C# hides functions 'By name and signature' - void f(int); in a derived class hides void f(int); in the base class, but not void f(double);

In Visual Basic (.NET), the programmer has a choice: the keyword Shadows specifies 'Hide by name' (which is the default for public methods) and the keyword Overloads specifies 'Hide by name and signature'

Thanks a lot. The last example by JLBorges settled the mess in my head
Topic archived. No new replies allowed.