Reputation: 4046
I'm not sure if template type deduction enters the fray here. But let me explain.
I have a template class EventProducer
from which objects that raise events derive, once for every event type, so that I can have something like this: class HellRaiser: public EventProducer<MouseMoveEvent>, public EventProducer<MouseButtonEvent>
. This seems to work just fine, except that you have to qualify the EventProducer
calls with the base type when calling through an object of the derived class. But shouldn't the compiler automatically deduce the base EventProducer
?
This is the definition:
template <typename TEvent>
class EventProducer
{
public:
typedef function<typename TEvent::HandlerSignature> HandlerFunction;
// Methods.
connection Subscribe(const HandlerFunction& callback);
void Trigger(TEvent& event);
protected:
// ...
};
Where TEvent::HandlerSignature
is defined in the event class.
hellRaiserObject->Trigger(MouseMoveEvent(11, -4));
assuming the object derives from EventProducer<MouseMoveEvent>
and EventProducer<MouseButtonEvent>
?hellRaiserObject->Subscribe(mouseCallback);
. I understand it may be a bit harder to deduce the type here, but mouseCallback
has a specific signature that can be compared to the template specializations, and it only fits one of them, I think.Just in case it makes a difference, events are defined like this: class MouseMoveEvent: public Event<MouseMoveEvent>
, where the base template is
template <typename TEventArgs>
class Event
{
public:
typedef TEventArgs EventArgs;
typedef void HandlerSignature(TEventArgs&);
// ...
};
Help much appreciated.
Upvotes: 3
Views: 182
Reputation: 3778
This is not specific to template:
#include <string>
class HandlerString
{
public:
void Handle(const std::string& event) {}
};
class HandlerInt
{
public:
void Handle(int event) {}
};
class ConcreteHandler : public HandlerString, public HandlerInt {};
int main(int argc, char const *argv[])
{
ConcreteHandler handler;
handler.Handle(std::string("foo"));
handler.Handle(1);
return 0;
}
Output of g++ 4.8.1:
test.cpp: In function 'int main(int, const char**)':
test.cpp:21:10: error: request for member 'Handle' is ambiguous
handler.Handle(std::string("foo"));
^
test.cpp:12:7: note: candidates are: void HandlerInt::Handle(int)
void Handle(int event) {}
^
test.cpp:6:7: note: void HandlerString::Handle(const string&)
void Handle(const std::string& event) {}
^
test.cpp:22:10: error: request for member 'Handle' is ambiguous
handler.Handle(1);
^
test.cpp:12:7: note: candidates are: void HandlerInt::Handle(int)
void Handle(int event) {}
^
test.cpp:6:7: note: void HandlerString::Handle(const string&)
void Handle(const std::string& event) {}
^
Upvotes: 0
Reputation: 70516
Name lookup from multiple base classes has to be unambiguous
10.2 Member name lookup [class.member.lookup]
1 Member name lookup determines the meaning of a name (id-expression) in a class scope (3.3.7). Name lookup can result in an ambiguity, in which case the program is ill-formed.
There are a bunch of technical details in that section of the Standard about how the names from the various base classes are to be merged, but in the end, if an ambiguity occurs, the program is ill-formed. Hence, you need to qualify your name in order to resolve this.
Note that the best matching signature of the various functions would come into play during overload resolution, but that occurs only after a successful and unambiguous name lookup.
Upvotes: 3