継承をまたいだオーバーロードは出来ない!?
手っ取り早くコードから。
class Base { public: void foo(void) { cout << "base" << endl; } }; class Sub : public Base { public: void foo(int) { cout << "sub" << endl; } }; int main( void ) { Sub sub; sub.foo(); sub.foo(42); return 0; }
のコード、基底クラスのメンバ関数 foo(void) のオーバーロードとなる関数 foo(int) を派生先クラスで 定義した場合、直感的には (Base と Sub には is-a関係が成り立つので) 上手くハズなのですが、
main.cpp: In function `int main()': main.cpp:28: error: no matching function for call to `Sub::foo()' main.cpp:19: note: candidates are: void Sub::foo(int)
あらま、コンパイルエラーだ。びっくりです。 どうもオーバーロード関数の探索って、そのクラスに同じ名前があれば 基底クラスまで遡らない、 つまり派生クラスに foo という名前が一つでもあれば、 基底クラスの foo と言う名前の全てのものは隠蔽されてしまうみたい。――つくづく「驚き最大限の法則」な言語ですよ、C++って。
で、これどういう事かな、と調べたら、そのものズバリが Effectife C++ にのってました (第3版 33項 継承した名前を隠蔽しないようにしよう)。 うー、基本トラップでしたか。どーして、こう、忘れちゃうかな、あたしってば。...orz
* * *
こういう場合、隠蔽されてしまった関数を 派生先クラスで再定義!でもいいのですが、もっとエレガントな解決策もあるそうな。
class Base { public: void foo(void) { cout << "base" << endl; } }; class Sub : public Base { public: using Base::foo; void foo(int) { cout << "sub" << endl; } };
おぉう、using ですか...。これで Base の foo という名前のものは全て Sub のスコープで可視になります。
この解決方法、C++の設計と進化 によると、ネームスペースを導入したとき、「クラスはネームスペースの一種だね!」→「だったらusing宣言で名前追加できるよね!」→「昔からの厄介な問題、派生クラスがベースクラスの名前隠すヤツの解決につかえない?」なぁんて感じで using なんだそうです。
なんかこのノリって「抽象データ型と差分プログラミングと仮想関数があればOOP出来ちゃわない!?」によく似ている様な気が。