coding challenges · advanced

State Machine with std::variant

Microsoft state machine via std::variant. Model each state as its own struct (Idle, Active{job}, Fault{reason}) and the state as variant<Idle,Active,Fault> — state-specific data travels with the state, and invalid (state,data) combinations are unrepresentable. std::visit with the 'overloaded' lambda idiom acts on the current state and the compiler enforces exhaustiveness (forgetting an alternative is a compile error). Transitions are overloaded on(State,Event)->State functions plus a variadic catch-all that ignores invalid events; dispatch() std::visits to pick the overload and assigns the returned next state. The machine always holds exactly one valid state. It's O(1) and heap-free (states inline, sized to the largest alternative). Use enum+switch for trivial FSMs, the GoF State pattern (virtual classes) when states are open/extensible, or a table-driven FSM for large state spaces.

🔑 Key line

State machine with std::variant: each state is its own type (its data travels with it), illegal states are unrepresentable, and std::visit + the overloaded idiom forces exhaustive handling (compile error if you miss one). Transitions = on(State,Event)->State overloads + a catch-all; dispatch = visit then assign. O(1), heap-free; use the GoF State pattern instead when states are open/extensible.

The code

// State machine with std::variant — each state is its own TYPE,
// transitions return the next state. No enum, no switch, no invalid states.
struct Idle {};
struct Active { int job; };
struct Fault { std::string reason; };
using State = std::variant<Idle, Active, Fault>;
// Events
struct Start { int job; };
struct Trip { std::string why; };
struct Reset {};
// Transition table = overloaded visitor on (state, event).
struct Machine {
State on(Idle, Start e) { return Active{e.job}; }
State on(Active, Trip e) { return Fault{e.why}; }
State on(Fault, Reset) { return Idle{}; }
template<class S, class E> State on(S s, E) { return s; } // ignore others
template<class E> void dispatch(State& st, E ev) {
st = std::visit([&](auto s){ return on(s, ev); }, st);
}
};
// Act on the CURRENT state with a visitor (overload set):
void run(const State& st) {
std::visit(overloaded{
[](Idle) { /* sleep */ },
[](const Active& a){ work(a.job); },
[](const Fault& f){ alarm(f.reason); },
}, st);
}

What this lesson walks through

  1. 01Each state is its own TYPE (variant, not enum)
  2. 02std::visit + overloaded — act on the current state
  3. 03Transitions = on(state, event) → next state
  4. 04Run it — and invalid events are safely ignored
  5. 05Trade-offs — variant vs the State pattern

Model the state as variant<Idle, Active, Fault>. Each state is a struct carrying exactly its own data — Active has a job, Fault has a reason. An enum would pile that data into one shared struct where most fields are invalid; variant makes illegal states unrepresentable.

See it animated — step by step, at your own pace

Unlock the full interactive walkthrough of State Machine with std::variant and 100+ animated C++ interview lessons.

← Previous
Task Scheduler — Topo Sort + Critical Path
Next →
Generic Key-Value Store — any vs variant