It's simple: in general it can't.

Global analysis can help, but it will never be as powerful as an explicit final declaration. It can optimize by deducing static types, but it won't devirtualize usages when pointers are involved, and there's no other hint of the actual type.

The reason is simple: consider the simple case above, but add to the simple example an additional mechanism, a plugin system capable of loading dynamic libraries. If you build an executable today with a compiler which does the devirtualization automatically, that compiler will decide that no subclass exists, and so will devirtualize the calls to value().

Tomorrow you write a new plugin which provides the executable with instances of a new class C, derived from B and overriding the function value(). The calls to value() were devirtualized, so C::value() won't get called.

That's why current compilers, even if they can deduce a lot of information, can't really devirtualize and optimize those calls.