2016-09-01 19 views
0

空のベースクラスと空の派生クラス(それぞれ異なる機能を表していますが、ここではすべて省略しています) 。私はこれらの両方の機能を必要とする別のクラスを持ち、両方のインターフェイスから継承します。以下のセットアップは、Visual Studio 2015を使用してうまくコンパイルされますが、gcc 6またはclang3.8ではコンパイルできません。 gccがベース文句DerivedCGcc/Clangでエラーが発生しましたが、Visual Studioでエラーが発生しません。

gccのエラーの曖昧な基底クラスです:

error: ‘Base’ is an ambiguous base of ‘DerivedC’ 
std::cout << "derivedC.typeId()" << dc.typeId() << std::endl; 

クランエラー:私は(へのポインタ)にキャストする必要があり、これらのクラスの使用で

ambiguous conversion from derived class 'DerivedC' to base class 'const Base': 
class DerivedC -> class DerivedA -> class Base 
class DerivedC -> class DerivedB -> class Base 
    std::cout << "derivedC.typeId()" << dc.typeId() << std::endl; 

ベースDerivedCモデルへの(ポインタへの)モデルへのモデル。複数の継承を仮想化する場合は、避けたいdynamic_castを実行する必要があります。私がgccを作ることができ、私が取るべきダイヤモンドの方向性を理解する方法はありますか?私には、「DerivedA :: typeIdを使用する」がこれを処理しているように思えます(VSではそうですが(使用していない場合は不平を言います))。興味深いことに、VSはコードの上にマウスを置くと "あいまいな基本エラー"の問題を示します。ただし、コンパイルして、目的の値を返します。

ご迷惑をおかけして申し訳ございません。 マイク

コード:

#include <iostream> 

class Base 
{ 
public: 

int typeId() const { 
    return doTypeId(); 
} 
private: 
virtual int doTypeId() const = 0; 
}; 

class DerivedA : public Base 
{ 
public: 

private: 
virtual int doTypeId() const override { return 1; } 
}; 

class DerivedB : public Base 
{ 
public: 

private: 
virtual int doTypeId() const override { return 2; } 
}; 

class DerivedC : public DerivedA, public DerivedB 
{ 
public: 
using DerivedA::typeId; 
private: 
virtual int doTypeId() const override { return 3; } 
}; 

int main(void) { 
    auto da = DerivedA(); 
    std::cout << "DerivedA.typeId() = " << da.typeId() << std::endl; 
    auto db = DerivedB(); 
    std::cout << "DerivedB.typeId() = " << db.typeId() << std::endl; 
    auto dc = DerivedC(); 
    std::cout << "derivedC.typeId() = " << dc.typeId() << std::endl; 
} 
+0

仮想継承について読んでください。あなたはBaseの1つのインスタンスを行いますか、DerivedAでは1つのインスタンスを、DerivedBでは1つのインスタンスを作成しますか? –

+0

私は仮想継承について知っていますが、私が指摘しているように、これは避けたいところにdynamic_castを必要としています。私の質問です:これを介して、またはClang/gcc正しいことにするには、Visual Studioの正しいですか?その場合は、仮想継承を使用せずに動作させる方法があります。 – Mike

答えて

0

あなたは仮想継承を使用していないので、あなたはダイヤモンドを持っていません。もしDerivedC用のVテーブルは、仮想メソッドdoTypeIdのための2つのスロット()を有しているベース

 
     --------   -------- 
     | Base |   | Base | 
     | #1 |   | #2 | 
     --------   -------- 
      V    V 
     ------------ ------------ 
     | DerivedA | | DerivedB | 
     ------------ ------------ 
       V   V 
       ------------ 
       | DerivedC | 
       ------------ 

の2つのインスタンスを有します。どちらが派生CのdoTypeId()の定義をオーバーライドしますか? [実際の問題ではありませんが、この問題を解決すると対処しなければならない問題です]

「using」ステートメントは問題を解決しません。 DerivedAクラスのメソッドの宣言をusingが表示されているコンテキストに持ち上げます。 DerivedBのメソッドのシグネチャが異なる場合は、usingによって隠されますが、同じであるため、引き続き使用できます。

名前はDerivedCで宣言されていませんが、コンパイラは名前の由来を追跡しません。したがって、呼び出しはまだあいまいです。 Baseで宣言されたメンバーに電話するには、thisポインターとして何を渡すべきかを知る必要があります。

ボトムライン:仮想継承を使用します。

+0

"Derived CのdoTypeId()の定義はどちらですか?"どちらも。これはまったく問題ではありません。コンパイラは 'doTypeId'ではなく、(仮想ではない)' typeId'への呼び出しについて不平を言います。 –

+0

OK、それに応じて私の答えを編集しますが、問題は依然として各DerivedCにBaseの2つのインスタンスを持つことによって引き起こされ、その答えはまだ仮想継承です。 –

+0

私の質問には完全に答えは出ていませんでしたが、それを受け入れるのは公正だと思います。私は結論します:1)いいえ、あなたは仮想継承を使用して回避することはできません。 2)Visual Studioにはまだコードをコンパイルするバグがあります。 – Mike

関連する問題