多重継承

多重継承すると、いろんなところで曖昧性が生まれる。


メソッドのシグネチャの衝突は、

  • using でどこから派生した関数を使うのか明示する
  • オーバーライドする

のどちかによって解決する。
そうしないと、「曖昧だ」というコンパイルエラー。

実験

#include <iostream>
#include <string>
using namespace std;

struct A { virtual string f() { return "A"; } };
struct B { virtual string f() { return "B"; } };
// 名前空間の指定による曖昧性の解消
struct C : public A, public B { using A::f; };
// オーバーライドによる曖昧性の解消
struct D : public A, public B { virtual string f() { return "D"; } };

string g(A &a) { return "g: " + a.f(); }
string h(B &b) { return "h: " + b.f(); }

// 多重定義!
string F(A &a) { return "Fa"; }
string F(B &b) { return "Fb"; }

int main() {
  C c;
  cout << c.f() << endl;    // A::f
  cout << c.B::f() << endl; // B::f
  cout << g(c) << endl;     // A::f
  cout << h(c) << endl;     // B::f
  cout << endl;

  D d;
  cout << d.f() << endl;    // D::f
  cout << c.A::f() << endl; // A::f
  cout << c.B::f() << endl; // B::f
  cout << g(d) << endl;     // D::f
  cout << h(d) << endl;     // D::f
  cout << endl;

  // キャストが無いとコンパイル通らない。
  cout << F(static_cast<A&>(d)) << endl;
}

実行結果:

A
B
g: A
h: B
 
D
A
B
g: D
h: D
 
Fa

結果からわかる挙動

  • using を使った場合は、ちゃんと多態してくれる。
    • 派生クラスの中にメソッドの実体が無いので、親クラスのメソッドを隠すことはない。
  • オーバーライドした場合
    • 同じシグネチャを持つ親クラスのメソッド全てを隠す。
    • すなわち、オーバーライドするなら複数の文脈で使えるメソッドとして実装しなければならない。


なるほど、危険だという理由はこれか。