Why can't run-time polymorphism be solved at compile time?

前端 未结 6 793
情书的邮戳
情书的邮戳 2020-12-07 14:27

Consider:

#include
using namespace std;

class Base
{
    public:
        virtual void show() { cout<<\" In Base \\n\"; }
};

class Der         


        
6条回答
  •  醉梦人生
    2020-12-07 14:41

    There are many reasons why compilers cannot in general replace the runtime decision with static calls, mostly because it involves information not available at compile time, e.g. configuration or user input. Aside from that, I want to point out two additional reasons why this is not possible in general.

    First, the C++ compilation model is based on separate compilation units. When one unit is compiled, the compiler only knows what is defined in the source file(s) being compiled. Consider a compilation unit with a base class and a function taken a reference to the base class:

    struct Base {
        virtual void polymorphic() = 0;
    };
    void foo(Base& b) {b.polymorphic();}
    

    When compiled separately, the compiler has no knowledge about the types that implement Base and thus cannot remove the dynamic dispatch. It also not something we want because we want to able to extend the program with new functionality by implementing the interface. It may be possible to do that at link time, but only under the assumption that the program is fully complete. Dynamic libraries can break this assumption, and as can be seen in the following, there will always be cases were it is not possible at all.

    A more fundamental reason comes from Computability theory. Even with complete information, it is not possible to define an algorithm that computes if a certain line in a program will be reached or not. If you could you could solve the Halting Problem: for a program P, I create a new program P' by adding an additional line to the end of P. The algorithm would now be able to decide if that line is reached, which solves the Halting Problem.

    Being unable to decide in general means that compilers cannot decide which value is assigned to variables in general, e.g.

    bool someFunction( /* arbitrary parameters */ ) {
         // ...
    }
    
    // ...
    Base* b = nullptr;
    if (someFunction( ... ))
        b = new Derived1();
    else
        b = new Derived2();
    
    b->polymorphicFunction();
    

    Even when all parameters are known at compile time, it is not possible to prove in general which path through the program will be taken and which static type b will have. Approximations can and are made by optimizing compilers, but there are always cases where it does not work.

    Having said that, C++ compilers try very hard to remove dynamic dispatch because it opens many other optimization chances mainly from being able to inline and propagate knowledge through the code. If you are interesting, you can find an interesting serious of blog posts for the GCC devirtualization implementation.

提交回复
热议问题