torutkのブログ

ソフトウェア・エンジニアのブログ

基底クラスのメンバ関数のオーバーロードを派生クラスで定義すると‥

C++言語における名前隠ぺいは、関数の場合引き数に関係なく名前の一致のみで発生するため、派生クラスの定義に基底クラスのメンバー関数のオーバーロードを記述すると、基底クラスの同名関数が見つからないコンパイルエラーが発生します。(「Effective C++第3版」33項、「C++の設計と進化」3.5.3項)

コンパイルエラーとなる派生クラスでのオーバーロード

派生クラスでメンバ関数オーバーロード定義することによって名前が隠ぺいされてしまうサンプルを以下に示します。

class Base {
public:
  virtual void mf();
  ...
};
class Derived : public Base {
public:
  virtual void mf(int v);
  ...
};

ごく普通の発想で、Baseクラスのメンバ関数mfの引数が異なるオーバーロード関数をDerivedで定義します。利用者に対するインタフェースを、利用者のカテゴリに応じて何段階かに分ける設計をするときがあります。

ここで、Derivedを呼び出すコードで、意図しないコンパイルエラーが発生します。

void useDerived() {
  Base* bas = new Derived;
  bas->mf(123); // OK
  bas->mf();    // OK

  Derived* der = new Derived;
  der->mf(123); // OK
  der->mf();    // コンパイルエラー
}

GCCでのエラーは以下となります。

main.cpp:29: error: no matching function for call to ‘Derived::mf()’

Derivedクラスのオーバーロード定義を削除すると、Baseクラスのメンバ関数が可視になるので、mf()のコンパイルエラーはなくなります。

コンパイルエラーとならない派生クラスでのオーバーロード

解決方法は、usingキーワードで基底クラスの名前を宣言することです。

class Base {
public:
  virtual void mf();
  ...
};
class Derived : public Base {
public:
  using Base::mf;  // コレを追加するとBase::mf()が見える
  virtual void mf(int v);
  ...
};