StateMachine with std::variant, getting the custom template deduction right

397 Views Asked by At

With the following code how do I write the custom template deduction correctly?

template<class R, class State, class... Ts> struct visitor : Ts... { using  Ts::operator()...; };
template<class R, class State, class... Ts> visitor(State, Ts...)->visitor<class R, State,Ts...>;

using Event = std::variant<HeartBeat, ConfigurationRead>;
using State = std::variant<Idle, Starting, Running, Error>;

void case3()
{
    Event e;
    State currentState;
    State newState = std::visit( visitor{
        [](Idle&& state, HeartBeat event) {std::cout << "Currently stopped, heartbeat received.\n"; return Error{}; }
    }, currentState, e);
}

I have looked at several examples, but I cannot find one which uses the return of the std::visit.

1

There are 1 best solutions below

1
On BEST ANSWER

Your visitor does not need to deduce and encode the return type R -- one will be inferred by std::visit itself: deduced from the callable or fixed to a specified template argument (see below). That being said, it could be simplified to:

template <typename... Ts> struct visitor : Ts... { using Ts::operator()...; };
template <typename... Ts> visitor(Ts...) -> visitor<Ts...>;

In each visitor must, however, return the same type. You could avoid any type mismatch by, e.g., explicitly specifying one for each lambda expression:

State newState = std::visit( visitor{
        [](Idle state, HeartBeat event) -> State { return Error{}; },
        //                                 ~~~~^
        [](auto state, auto event) -> State { return state; }
        //                            ~~~~^
    }, currentState, e);

DEMO


In , the return type can be specified as the first template argument of std::visit:

State newState = std::visit<State>( visitor{
        //                  ~~~~^
        [](Idle state, HeartBeat event) { return Error{}; },
        [](auto state, auto event) { return state; }
    }, currentState, e);