I managed a team of C/C++ programmers for many years in Silicon Valley. I always encouraged my engineers to write clean code without fancy tricks. Fancy tricks lead to bugs that spend a lot of time to debug. If I had an engineer write that dispatch() function with the vtable, I'd have beaten them with a wet noodle until they promised never to do that again.
You still need to select the correct table for every incoming packet though. But again C++ has idiomatic ways for that, e.g. std::unordered_map<uint8_t, IPacketHandler* > for sparse values, or std::array<IPacketHandler* , n> for dense zero-based values.
While this approach is slightly more complex (need to register handlers somehow), debuggers are happy with that kind of dynamic dispatch because standard OO C++. IMO over time it’s more maintainable, e.g. it’s trivial to add another handling method, and the compiler will check that you implement that for each protocol you support.