Today a co-worker of mine ran into an interesting C++ inheritance problem that perplexed the usual lunch crowd. Some might call this a character flaw, but when presented with puzzles like this I feel compelled to figure them out. So, when I got back to my desk I reduced the problem to the following minimal program and got to work on figuring out the root-cause.
1: class Superclass 2: { 3: public: 4: void foo(int aParameter); 5: void foo(int oneParameter, 6: int twoParameter); 7: }; 8: 9: class Subclass : public Superclass 10: { 11: void foo(int aParameter); 12: // inherits foo(int, int) 13: }; 14: 15: void Superclass::foo(int aParameter) 16: {} 17: 18: void Superclass::foo(int oneParameter, 19: int twoParameter) 20: {} 21: 22: void Subclass::foo(int aParameter) 23: { 24: foo(aParameter, 2); 25: } 26: 27: int main(int argc, char * argv[]) 28: { 29: return (0); 30: }
In words, there is a super-class, Superclass
, with an overloaded
method, foo()
, that has two variants - foo(int)
and
foo(int,int)
. In addition, there is a sub-class, Subclass
, derived
from Superclass
that redefines the foo(int)
variant but inherits
Superclass
's foo(int,int)
method.
Straight forward, right? Nope.
$g++ borked.cpp borked.cpp: In member function ‘void Subclass::foo(int)’: borked.cpp:26: error: no matching function for call to ‘Subclass::foo(int&, int)’ borked.cpp:24: note: candidates are: void Subclass::foo(int)
For some reason the compiler isn't able to resolve the call to the
inherited foo(int,int)
when called from Subclass
's foo(int)
method. How strange.
Commenting out the offending call on line 24 allows the program to compile and dumping the symbol table yields the expected results:
$nm a.out 0000000100000ea4 s stub helpers 0000000100001048 D _NXArgc 0000000100001050 D _NXArgv 0000000100000e60 T __ZN10Superclass3fooEi 0000000100000e6e T __ZN10Superclass3fooEii 0000000100000e7e T __ZN8Subclass3fooEi U ___gxx_personality_v0 0000000100001060 D ___progname 0000000100000000 A __mh_execute_header 0000000100001058 D _environ U _exit 0000000100000e8b T _main 0000000100001020 s _pvars U dyld_stub_binder 0000000100000e24 T start
All the methods are present and have unique signatures. Recompiling
with the -fdump-class-hierachy
option also doesn't produce any
suprises - there is no vtable magic going on.
$g++ -fdump-class-hierarchy borked.cpp $cat borked.cpp.002t.class Class Superclass size=1 align=1 base size=0 base align=1 Superclass (0x1407c18c0) 0 empty Class Subclass size=1 align=1 base size=1 base align=1 Subclass (0x1407c1930) 0 empty Superclass (0x1407c19a0) 0 empty
As a final piece of the puzzle, fully qualifying the call to
foo(int,int)
as follows resolves the problem.
void Subclass::foo(int aParameter) { Superclass::foo(aParameter, 2); }
Now, I am no C++ guru but I do have a fair amount of experience with it - mostly from writing system level code for a proprietary RTOS that was written in C++. In fact, I wrote a loadable module framework for that RTOS that included a runtime dynamic linker capable of handling C++ intermediate object files. So, I'd like to think I have a decent knowledge of the C++ object model and yet this problem made little sense to me.
As is often the case, I didn't know the anwser to the problem but I
did know enough to determine the right keywords to Google for the
answer. The solution is to add a using
declaration to the Sublcass's
definition.
class Subclass : public Superclass { using Superclass::foo; void foo(int aParameter); // inherits foo(int, int) };
As I now understand the situation, when a set of overloaded methods span
the base and derived classes, the using
declaration is required to
introduce the base class's methods into the scope of the derived class.
Without the using
declaration, the compiler can't resolve the reference
to the base class's method without fully qualifying it. Good to know!
I had a lot of fun figuring this out despite the fact that it took 20 minutes of time out of an already busy day. But, sometimes you just have to let the inner-geek exercise itself!
As an aside, I recommend Stanley Lippman's book Inside the C++ Object Model to anyone interested in learning the inner workings of the C++ object model.