2016-03-05 57 views
0

我正在使用stdlib來練習一個簡單的事件系統。對繼承模板方法的模糊調用

我做了一個EventEmitter<T>班,它提供了addListener(T)emitEvent(T)

有一個TestEmitter類延伸EventEmitter<int>EventEmitter<std::string>TestEmitter類有一個emitTestEvent方法,該方法調用emitEvent(1337)emitEvent(std::string("test"))

在main()中創建TestEmitter的實例,併爲T = int和T = std :: string添加兩個偵聽器。

我得到了4個看起來像這樣的錯誤:reference to ‘emitEvent’ is ambiguous。撥打電話emitEvent兩次,撥打addListener兩次。

下面是完整的輸出:

main.cpp: In member function ‘virtual void TestEmitter::emitTestEvent()’: 
main.cpp:42:4: error: reference to ‘emitEvent’ is ambiguous 
main.cpp:23:15: error: candidates are: void EventEmitter<T>::emitEvent(T) [with T = std::basic_string<char>] 
main.cpp:23:15: error:     void EventEmitter<T>::emitEvent(T) [with T = int] 
main.cpp:45:4: error: reference to ‘emitEvent’ is ambiguous 
main.cpp:23:15: error: candidates are: void EventEmitter<T>::emitEvent(T) [with T = std::basic_string<char>] 
main.cpp:23:15: error:     void EventEmitter<T>::emitEvent(T) [with T = int] 
main.cpp: In function ‘int main(int, char**)’: 
main.cpp:53:10: error: request for member ‘addListener’ is ambiguous 
main.cpp:19:15: error: candidates are: void EventEmitter<T>::addListener(EventEmitter<T>::Listener) [with T = std::basic_string<char>; EventEmitter<T>::Listener = std::function<void(std::basic_string<char>)>] 
main.cpp:19:15: error:     void EventEmitter<T>::addListener(EventEmitter<T>::Listener) [with T = int; EventEmitter<T>::Listener = std::function<void(int)>] 
main.cpp:57:10: error: request for member ‘addListener’ is ambiguous 
main.cpp:19:15: error: candidates are: void EventEmitter<T>::addListener(EventEmitter<T>::Listener) [with T = std::basic_string<char>; EventEmitter<T>::Listener = std::function<void(std::basic_string<char>)>] 
main.cpp:19:15: error:     void EventEmitter<T>::addListener(EventEmitter<T>::Listener) [with T = int; EventEmitter<T>::Listener = std::function<void(int)>] 

我以很困惑,爲什麼我收到此錯誤,編譯器(G ++)清楚地知道什麼T是無論是通話,但它不知道哪種方法可以調用?

我能夠用EventEmitter<int>::EventEmitter<std::string>前面加上他們解決在emitEvent呼叫的錯誤,但我不知道這是否是解決這個問題的正確方法。

當編譯器知道T的類型時,爲什麼emitEventaddListener的調用不明確?我怎樣才能解決這個問題?

我希望我提供了足夠的信息,如果不讓我知道。下面的代碼:

#include <list> 
#include <map> 
#include <string> 
#include <functional> 
#include <iostream> 

template<typename T> 
class EventEmitter { 
private: 
     typedef std::function<void(T)> Listener; 

protected: 
     std::list<Listener> listeners_; 

public: 
     EventEmitter() {}; 
     virtual ~EventEmitter() {}; 

     virtual void addListener(Listener listener) { 
       listeners_.push_back(listener); 
     }; 

     virtual void emitEvent(T event) { 
       typename std::list<Listener>::iterator it = listeners_.begin(); 
       for(; it != listeners_.end(); ++it) { 
         (*it)(event); 
       } 
     }; 
}; 

class TestEmitter : public EventEmitter<int>, public EventEmitter<std::string> { 
private: 
     int count_; 

public: 
     TestEmitter() {}; 
     virtual ~TestEmitter() {}; 

     virtual void emitTestEvent() { 
       count_ = (++count_) % 2; 
       if(count_ == 0) { 
         EventEmitter<int>::emitEvent(1337); 
       } 
       else { 
         EventEmitter<std::string>::emitEvent(std::string("test")); 
       } 
     }; 
}; 

int main(int argc, char* argv[]) { 
     TestEmitter emitter; 

     emitter.addListener([](int event) { 
       std::cout << "Hello: " << event << std::endl; 
     }); 

     emitter.addListener([](std::string event) { 
       std::cout << "Bye: " << event << std::endl; 
     }); 

     for(int i = 0; i < 10; i++) { 
       emitter.emitTestEvent(); 
     } 

     return 0; 
} 

謝謝:)

+0

編譯器不能找出哪些繼承的方法調用。我不知道爲什麼的確切細節,但一個解決方法是調用'emitter.EventEmitter :: addListener([](int event){/ * ... * /});''和'emitter.EventEmitter :: addListener([](std :: string event){/ * ... * /});'改爲。 – jotik

+0

'count_ =(++ count_)%2;'也有未定義的行爲,因爲序列點規則。取而代之的是使用'count_ =(count_ + 1)%2;' – jotik

+0

@jotik感謝解決工作:)我會研究序列點規則,我不知道!謝謝你讓我知道。 – Bastiaanus

回答

0

你的類繼承了兩個不同的其他類。現在有兩種方法:EventEmitter :: emitTestEvent和EventEmitter :: emitTestEvent

哪一個應該被調用?這本身並不明確。

你可以做到以下幾點:

emitter.EventEmitter<int>::emitEvent(/* ... */); 

但是,您可能overthink您的設計...什麼應該做eventEmit,當你的類的用戶調用呢?然後,相應地設計您的課程,以便他們提供這種行爲。

編輯: 另一個解決方案可以通過using裝置中找到,例如:

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

class A 
{ 
public: 
    void foo(int) {cout << "A::foo";} 
}; 

class B 
{ 
public: 
    void foo(string) {cout << "B::foo";} 
}; 

class C : public A, public B 
{ 
public: 
    using A::foo; 
    using B::foo; 
}; 

int main() { 
    C c; 
    c.foo(10); 
    c.foo("hi"); 
    return 0; 
} 
+0

我的想法是事件監聽器將通過其參數進行標識。那麼,要調用哪個'emitEvent'方法將取決於給定的參數。使用'MessageReceivedEvent'類型的1個參數調用'emitEvent'會調用該特定類型的所有註冊偵聽器,如果這樣做合理的話。我可以創建一個'Event'基類並創建偵聽器,以在運行時檢查給定事件的類型,但我不想使用'typeid'或'dynamic_cast'。也許我正在超越它...我會接受這個答案,因爲它確實解決了我的問題(也感謝@jotik)。 – Bastiaanus