9

私は、次の例に要約することができます実際の状況を持っている:あいまいな多重継承

template< typename ListenerType > 
struct Notifier 
{ 
    void add_listener(ListenerType&){} 
}; 

struct TimeListener{ }; 
struct SpaceListener{ }; 

struct A : public Notifier<TimeListener> 
     , public Notifier<SpaceListener> 
{ 

}; 

struct B : TimeListener{ }; 

int main() 
{ 
    A a; 
    B b; 

    a.add_listener(b); // why is ambiguous? 

    return 0; 
} 

なぜBTimeListenerあるコンパイラに明らかにされていませんので、のみ可能過負荷の解像度はNotifier<TimeListener>::add_listener(TimeListener&)ですか?

+4

あなたは '通知機能を使用して、あなたの問題を解決することがあります:: add_listener; '(そしてもう一方は)' struct A'の中にあります。 [デモ](http://coliru.stacked-crooked.com/a/6e43848691a4cfcb) – Jarod42

答えて

8

メンバー名のルックアップ規則はあなたのコードが曖昧であることを言う:私は現在、私のコンピュータでこれをテストすることはできませんが、コンパイラは困難あなたの元の例では、シンボルの解決を持っている場合はそれが動作するはずですnameは2つの基本クラスにあり、ルックアップセットは無効です。ルックアップセットとマージのすべての詳細に精通する必要はありません。重要な詳細は、両方の基本クラスがチェックされており、どちらも曖昧さを招く名前add_listenerが見つかりました。

簡単な修正は、using-declarationsを使用してこれらの基本クラス名をAにすることです。これはadd_listenerの両方のバージョンは、むしろ基底クラスよりも、Aで検索されていることを意味するので、何もマージあいまいさがありません:

struct A : public Notifier<TimeListener> 
     , public Notifier<SpaceListener> 
{ 
    using Notifier<TimeListener>::add_listener; 
    using Notifier<SpaceListener>::add_listener; 
    //plus any more base classes 
}; 

Live Demo

5

標準の指示されたコンパイラは、シンボルを解決するほどスマートではありません。論理的にこのインスタンスでそれを実行できるという事実にもかかわらず、あいまいな操作として定義されています。あなたのコンパイラはおそらくシンボル名だけを探していますが、プロトタイプには両方の可能なシンボルが見つかった後ではありません。

受け入れるはずのテンプレートシンボルを明確にすることで、両方の型を明示的に受け入れることをコンパイラに伝えることができます。これにより、どちらかのフォームを受け入れてからテンプレートを適用するようコンパイラーに指示されます。以下はその例です。ので、

struct A : public Notifier<TimeListener> 
     , public Notifier<SpaceListener> 
{ 
    using Notifier<TimeListener>::add_listener; 
    using Notifier<SpaceListener>::add_listener; 
}; 
+6

コンパイラが十分にスマートではないということではなく、標準ではこれがあいまいであると言われています。 – TartanLlama

+0

良い点。私は定義された曖昧さをカプセル化するのに十分なほどスマートではないということを明確にしようとしました。 – Pyrce