So the existence of virtual function tables is not a differentiator between C++ and C.
The data types with virtual function tables are just the implementation method for sum types that is dual to tagged unions. Both virtual function tables and tagged unions can be implemented in C and in most other programming languages that do not have intrinsic support for them, but they require explicit boilerplate code for invoking the virtual functions or for testing the union tags.
Which is the better of these 2 variants depends on the application. In high-performance computations, one does not use ambiguous data types, so normally none of these 2 is used. There are a few object-oriented programming languages where "everything is an object", i.e. any kind of data includes a virtual table pointer, but those are just incomplete programming languages, which do not have all the data types needed in practice, like also many early programming languages that had a unique data type, e.g. the original LISP I, which had only linked lists and no arrays, etc. C++ at least is a complete language, in which any kind of data type can be implemented, without overheads.
As you said previously, C has few restrictions in what it can do, so in theory it is almost always possible to write a C program almost exactly equivalent with any program written in another language, matching its speed, even if that may require a significant reorganization of the code, not a line to line translation.
Nevertheless, as the other poster said, the effort needed to write that equivalent program may be so high that it is not a realistic solution.
So in practice it is not unusual that at similar programming efforts a higher-level language like C++ frequently allows writing a faster program than C.
For dynamic dispatch there is absolutely no difference between using a jump table in C and virtual method tables in C++. If the compiler can infer the target address at compile time, it will not go through an indirect call, e.g.:
https://www.godbolt.org/z/as8ehGhv3
And for 'static dispatch' there's no difference between a C++ method call and a direct C function call (since for static dispatch the caller needs to 'know' the target function either way).
https://cpplove.blogspot.com/2012/07/a-generic-loop-unroller...
Of course, nothing beats hand written ffmpeg-style assembly which takes into account optimal register allocation, instruction scheduling, cache alignment, etc. for specific processor architectures.
The metaprogramming facilities of C++ allow the programmer to more effectively optimise than they would have the patience to do in C.
The compiler's own optimisations don't directly benefit from the metaprogramming facilities in this sense. What they do is let the programmer break high level constructs down to codegen that the compiler can optimise
And you could do the same things by hand in C or Rust, but it would be tedious in the extreme, and you'd probably find yourself adopting external codegen tools