継承をまたいだオーバーロードは出来ない!?

手っ取り早くコードから。

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出来ちゃわない!?」によく似ている様な気が。