I'm playing around with a templated implementation of an FSM and am encountering an ambiguity as follows:
/home/permal/code/FSM/Test/../FSM/dist/include/FSM.h: In instantiation of ‘void fsm::FSM<FSMBaseState>::Event(std::unique_ptr<EventType>) [with EventType = AddEvent; FSMBaseState = EventBaseState]’:
/home/permal/code/FSM/Test/test.cpp:83:44: required from here
/home/permal/code/FSM/Test/../FSM/dist/include/FSM.h:59:4: error: request for member ‘Event’ is ambiguous
myCurrent->Event( std::move( event ) );
^
In file included from /home/permal/code/FSM/Test/../FSM/dist/include/FSM.h:12:0,
from /home/permal/code/FSM/Test/test.cpp:8:
/home/permal/code/FSM/Test/../FSM/dist/include/EventReceiver.h:15:15: note: candidates are: void fsm::EventReceiver<EventType>::Event(std::unique_ptr<_Tp>) [with EventType = SubtractEvent]
virtual void Event( std::unique_ptr<EventType> event ) = 0;
^
/home/permal/code/FSM/Test/../FSM/dist/include/EventReceiver.h:15:15: note: void fsm::EventReceiver<EventType>::Event(std::unique_ptr<_Tp>) [with EventType = AddEvent]
In file included from /home/permal/code/FSM/Test/test.cpp:8:0:
/home/permal/code/FSM/Test/../FSM/dist/include/FSM.h: In instantiation of ‘void fsm::FSM<FSMBaseState>::Event(std::unique_ptr<EventType>) [with EventType = SubtractEvent; FSMBaseState = EventBaseState]’:
/home/permal/code/FSM/Test/test.cpp:91:50: required from here
/home/permal/code/FSM/Test/../FSM/dist/include/FSM.h:59:4: error: request for member ‘Event’ is ambiguous
myCurrent->Event( std::move( event ) );
So my question is why the compiler cannot choose the correct option even though it spells out that there are two possible candidates, one of which is the correct one.
I hope the code below is enough to show the problem, the rest is available on GitHub
This is the class and method where the ambiguity occurs:
template<typename FSMBaseState>
class FSM
{
public:
...
template<typename EventType>
void Event( std::unique_ptr<EventType> event )
{
if( HasState() )
{
// This way of calling causes ambiguous method lookup during template instantiation
myCurrent->Event( std::move( event ) );
// casting to the correct type works, but is it really needed?
// auto* s = myCurrent.get();
// static_cast<EventReceiver<EventType>*>( s )->Event( std::move( event ) );
}
}
};
The above method is called either as fsm.Event( std::make_unique<AddEvent>() );
or fsm.Event( std::make_unique<SubractEvent>() );
myCurrent
in the above Event()-method is an instance of the following class:
class EventBaseState
: public fsm::BaseState<EventBaseState>,
public fsm::EventReceiver<AddEvent>,
public fsm::EventReceiver<SubtractEvent>
{
public:
EventBaseState( const std::string& name, fsm::FSM<EventBaseState>& fsm ) :
BaseState( name, fsm )
{}
};
where EventReceiver
is defined as follows:
template<typename EventType>
class EventReceiver
{
public:
virtual void Event( std::unique_ptr<EventType> event ) = 0;
};
Update
Looking at things from a new angle, I ended up with a pure virtual functions for the Event<T>(...)
methods, which is actually what I really wanted as the EventBaseState
class was supposed to be abstract.
class EventBaseState
: public fsm::BaseState<EventBaseState>,
public fsm::EventReceiver<AddEvent>,
public fsm::EventReceiver<SubtractEvent>
{
public:
EventBaseState( const std::string& name, fsm::FSM<EventBaseState>& fsm ) :
BaseState( name, fsm )
{}
virtual void Event( std::unique_ptr<AddEvent> event ) override = 0;
virtual void Event( std::unique_ptr<SubtractEvent> event ) override = 0;
};
Of course, the solution provided by Chajnik-U works too.