これは実際のコードを簡略化したものであり、誰かが既に Foo を実装し、そこから派生していることに気付いていなかったときに犯した本当の間違いです。
#include <iostream>
struct Base {
virtual ~Base() { }
virtual void print() = 0;
};
struct OtherBase {
virtual ~OtherBase() { }
};
struct Foo : public Base { // better to use virtual inheritance?
virtual void print() { std::cout << "Foo" << std::endl; };
};
struct Bar : public Base { // better to use virtual inheritance?
virtual void print() { std::cout << "Bar" << std::endl; };
};
// The design is only supposed to implement Base once, but I
// accidentally created a diamond when I inherited from Bar also.
class Derived
: public OtherBase
, public Foo
, public Bar // oops.
{
};
int main() {
Derived d;
OtherBase *pO = &d;
// cross-casting
if (Base *pBase = dynamic_cast<Base *>(pO))
pBase->print();
else
std::cout << "fail" << std::endl;
}
編集:このコードを実行する必要がないように...
- そのまま実行すると、「失敗」(望ましくない、デバッグが難しい) が出力されます。
- 「oops」とマークされた行を削除すると、「Foo」(望ましい動作) が出力されます。
- 「おっと」を残して 2 つの継承を仮想化すると、コンパイルされません (ただし、少なくとも何を修正すればよいかはわかっています)。
- 「oops」を削除して仮想化すると、コンパイルされて「Foo」が出力されます (望ましい動作)。
仮想継承を使用すると、結果は良好またはコンパイラ エラーになります。仮想継承がなければ、結果は良好であるか、説明がつかず、デバッグが困難な実行時障害になります。
Foo が既に行っていたことを基本的に複製する Bar を実装すると、動的キャストが失敗し、実際のコードでは問題が発生しました。
最初は、コンパイル エラーがないことに驚きました。次に、仮想継承がないことに気付きました。これにより、GCC で「一意の最終オーバーライド機能がありません」というエラーが発生しました。この設計にはひし形が含まれていないはずなので、意図的に仮想継承を使用しないことにしました。
しかし、Base から派生するときに仮想継承を使用していれば、コードは (おっともなく) 正常に機能し、コンパイル時にダイアモンドについて警告され、実行時にバグを追跡する必要があったはずです。
質問は、仮想継承を使用して将来同様の間違いを犯さないようにすることは許容できると思いますか? ここで仮想継承を使用する正当な技術的理由 (私にはわかります) はありません。設計にひし形があってはならないからです。その設計上の制約を強制するためだけに存在します。