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'中。 [Demo](http://coliru.stacked-crooked.com/a/6e43848691a4cfcb) – Jarod42

回答

8

成員名稱查找規則說,你的代碼是模糊的,因爲名稱在兩個基類中找到,因此查找集合無效。您不需要熟悉查找集和合並的所有細節;重要的細節是檢查兩個基類,並在兩個中找到名稱add_listener,這會產生歧義。

簡單的解決方法是使用using聲明將這些基類名稱帶入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