I spent the first bit of this article going "Wait isn't that just what a finite state machine is?" and then was subsequently super satisfied by the next bit where they say that this is indeed a really good way to handle the general case Reducing program logic to a small state machine and business logic that interacts with it in predefined ways is a pattern I learned while doing asynchronous netcode for a game project…
It's quite sad that generał purpose language designers are not yet at the stage of daring to provide any convenient syntax for high level concepts like state machines or entity component systems.
Then, you can use any container you like to keep track of your valid state transitions and not even have to use polymorphism. But of course C++ gives you many ways in which to trivially break this nice system of guarantees, including mutable lambdas, or even just implicit reliance on fixed memory addresses via explicit allocation and pointers in an external scope. This pattern is very general, so it's hard to capture in a guardrail