Kristian D'Amato
Kristian D'Amato

Reputation: 4046

Base resolution/template type deduction

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.

  1. Why is this call ambiguous: hellRaiserObject->Trigger(MouseMoveEvent(11, -4)); assuming the object derives from EventProducer<MouseMoveEvent> and EventProducer<MouseButtonEvent>?
  2. Can I do something to help the compiler decide which to call, without explicitly specifying the base?
  3. The same happens with this call: 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

Answers (2)

Johan
Johan

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

TemplateRex
TemplateRex

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

Related Questions