C++ – the difference between compile time polymorphism and static binding

bindingc++

This link helped me understand the difference between static binding and dynamic binding? but I am in a confusion that

what is the difference between static binding and compile time polymorphism or no difference is there.

This also creates a doubt about dynamic binding and runtime polymorphism?

Best Solution

In short

Static vs dynamic binding is about when the exact code to run (i.e. the function's address) is known: at compile-, link- (both "static"), load-, or run-time (both "dynamic").

Polymorphism is firstly about how the exact code to run is known: to qualify as polymorphism, it's necessarily inferred from the type of data being processed. When the "dynamic type" of data is not known until run-time (often because the type is determined by run-time data inputs) dynamic binding must be used, and that requires dynamic polymorphism (aka runtime polymorphism; C++ offers the virtual dispatch mechanism in this category). There are other situations when virtual dispatch is useful even though the types of data being processed are available at compile time - particularly for minimising/eliminating (re)compile times after code changes, and for tuning code "bloat". Anyway, compile-time aka static polymorphism uses what's known about the types at compile time to bind at compile- or link- time (i.e. "statically").

Examples

struct Base { virtual void f(); void g(); };
struct Derived : Base { void f(); void g(); };

Derived d;
d.f();    // if definition's in a shared library, needs dynamic binding
          // otherwise (same translation unit, linked object, static lib)
          // compiler should optimise to static binding
          // (though functionally either would work)

Base* p = factory(data);
p->f();   // dynamic binding - if p points to a Base, use Base::f()
          //                 - if p pointer to a Derived, use Derived::f()

void some_func(const char*);  // note: no polymorphism / overloads

some_func("hello world\n");
       // if some_func is defined in...
       //  - shared / dynamic link library, binds dynamically
       //  - otherwise, static binding

std::cout << "hello world\n";  // static binding
                               // compile-time polymorphism from (operator) overloading

Discussion

binding normally refers to the time at which the program resolves a function call to a specific function implementation's machine code:

  • static means this happens during compilation

  • dynamic means this happens when the executable program is launched/running

"binding" - and "currying" - are also used to describe the stipulation of arguments to functors (search for "binding" in Stroustrup's C++11 FAQ)

The only situations in which C++ programs bind function calls dynamically are:

  • when a dynamic library is used, in which case the binding may be done by the Operating Systems loader before main() is called, or explicitly in code using dlsym (or similar OS-specific function), which returns a function pointer to use to call a function found in a dynamic library (.so, .dll, ...).

  • in virtual dispatch, when a virtual member function is found at run-time, typically by following a pointer from the data-object to a virtual dispatch table where the function pointer's recorded

  • when function pointers are used explicitly by the programmer

In other situations, binding is static: the compiler itself writes a jmp or call to a specific memory address/offset (whether absolute or relative to the Program Counter doesn't matter) into the object or executable it creates, and that is not modified during program loading or execution.

The static / dynamic binding classification only has a little overlap with polymorphism:

  • virtual dispatch normally uses dynamic binding (but can sometimes be optimised as per Examples above) and

  • all other forms of polymorphism in C++ (namely overloading, templates, inlined macro expansions) use static binding, but

  • so does most non-polymorphic code: any "normal" non-virtual call to a function that's not in a shared/dynamic library is resolved at compile time too.