I am working on a simple event system to practice using the stdlib.
I made an EventEmitter<T>
class which provides addListener(T)
and emitEvent(T)
.
There's a TestEmitter
class that extends EventEmitter<int>
and EventEmitter<std::string>
. The TestEmitter
class has an emitTestEvent
method that calls either emitEvent(1337)
or emitEvent(std::string("test"))
.
In main() an instance of TestEmitter
is created and two listeners are added for T=int and T=std::string.
I am getting 4 errors that look like this: reference to ‘emitEvent’ is ambiguous
. Twice on the emitEvent
calls and twice on the addListener
calls.
Here's the full output:
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)>]
I am quite confused as to why I am getting this error, the compiler (g++) clearly knows what T
is for either of the calls but it doesn't know which method to call?
I was able to fix the errors on the emitEvent
calls by prepending them with EventEmitter<int>::
and EventEmitter<std::string>
but I am not sure if this is the right way to fix this problem.
Why are the calls to emitEvent
and addListener
ambiguous when the compiler knows the type of T
? How can I fix this?
I hope I provided enough information, if not let me know. Here's the code:
#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;
}
Thanks :)
Your class inherits two different other classes. Now, there are two methods: EventEmitter::emitTestEvent and EventEmitter::emitTestEvent
Which one is supposed to be called? This is not clear in itself.
You can do the following:
emitter.EventEmitter<int>::emitEvent(/* ... */);
However, you might overthink your design... What should eventEmit do, when the user of your class calls it? Then, design your classes accordingly, such that they offer this behaviour.
Edit:
Another solution can be found by means of using
, e.g.:
#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;
}