How to Implement Static Polymorphism in C++
- Understand Curiously Recurring Template Pattern to Learn the Mechanism of Static Polymorphism in C++
- Understand Policy-Based Design to Learn the Usefulness of Static Mechanism in C++
- Implement Static Polymorphism Using Early Binding, Overloading, or Parametric Polymorphism in C++
The static polymorphism can be primarily interpreted in the context of C++. This tutorial will teach you the importance, usefulness, and how to implement static polymorphism in C++.
The std:sort
function from C++ is statically polymorphic because of its dependency on object-provided interface (objects which behave like iterations).
Furthermore, the exact behavior of provided iterations (objects) under the interface can be determined in the compile time.
Understand Curiously Recurring Template Pattern to Learn the Mechanism of Static Polymorphism in C++
Implementing static polymorphism is a good coding style; to do this, you need to learn its mechanics through CRTP. It’s a fact that you must sacrifice some flexibility of your C++ program for speed (through static polymorphism).
It shows technical polymorphism behavior, accomplishes different goals than its other types, and decides which piece of code to execute and prioritize based on the type of something.
Furthermore, it requires implementations that can result in binary code size bloat and inspection in each translation unit so that the compiler can understand the implementation.
#include <iostream>
template <class Self>
struct Primary {
Self& self() { return static_cast<Self&>(*this); }
// an algorithm to perform some function
int basic_function(int x) {
self().prologue();
if (x > 42) x = self().downsize(x);
x = self().crunch(x);
self().epilogue();
return x;
}
void prologue() {}
int downsize(int x) { return x % 42; }
int crunch(int x) { return -x; }
void epilogue() {}
};
struct secondary_Derived : Primary<secondary_Derived> {
int downsize(int x) {
while (x > 42) x /= 2;
return x;
}
void epilogue() {
std::cout << "CRTP__ \n"
<< "Process complete! \n";
}
};
int main() {
secondary_Derived obj_Dri;
std::cout << obj_Dri.basic_function(420);
}
Output:
CRTP__
Process complete!
-26
The CRTP allows the base class to provide information or data in terms of the derived class and its majority of use cases represent the same; e.g., Boost’s iterator_facade
.
The signatures
is a member function of CRTP and things like DerivedClass operator++() {/* Return *this after incrementation */}
are derived from this member function.
In case, your C++ code requires polymorphic output behavior should derived from std::sstream
vs std::fstream
vs std::cout
(even though its type is unspecified) are polymorphism and derived from ostream
. As CRTP is a form of F-bound
quantification, it is known as F-bound
polymorphism.
It enables the achievement of virtual functions without the cost of flexibility and is sometimes called simulated dynamic binding
.
Windows STL and WTL libraries use this function and the Barton-Nackman trick
is a similar use of static polymorphism, which is sometimes referred to as restricted template expansion
where common functionality can be placed in a base class that minimizes the C++ code redundancy.
Understand Policy-Based Design to Learn the Usefulness of Static Mechanism in C++
It’s one of the extremely powerful techniques to achieve static polymorphism in C++. In modern C++ design, the policy-based class or policy-based programming is a design approach based on an idiom for C++ known as policies.
The compile-time variant of the strategy pattern increases modularity and highlights orthogonal design decisions.
It defines the behavior of individual classes at a relatively low level representing innovation while assembling software components out of interchangeable modules is far from a new concept.
#include <windows.h>
#include <iostream>
struct color_code {
void pri_color() { this->reveal_colorCode(); }
void reveal_colorCode() {
std::cout << "The color code is revealed!" << std::endl;
}
};
struct banner_code {
void pri_color() {
std::cout << "The banner code is revealed to the officials!" << std::endl;
}
};
template <class code_type>
class Info : public code_type {
public:
void code_implementation() { code_type::pri_color(); }
};
int main() {
Info<color_code> lordcommander;
lordcommander.code_implementation();
Info<banner_code> kingslayer;
kingslayer.code_implementation();
return 0;
}
Output:
The color code is revealed!
The banner code is revealed to the officials!
It enables you to reinterpret patterns for compile time (e.g., template method pattern) so that the main class has a skeleton algorithm, which calls the appropriate functions of individual policies (at customization points).
Generally, these designs do not require inheritance, and most of their functions reflect static polyphormic behavior.
Implement Static Polymorphism Using Early Binding, Overloading, or Parametric Polymorphism in C++
Its object methods are invoked at compile time and are usually implemented using the operator and function overloading.
Method overloading is a compile-time polymorphism in which more than one method can have the same name but different parameter lists and types.
It enhances performance since its methods are known at compile time which makes the execution faster but leads to decreased flexibility in implementing solutions.
Function overloading and operator overloading reflect static polymorphism behavior as it provides an overloading facility to extend the code’s functionality.
#include <windows.h>
#include <iostream>
// parametric polymorphism
template <class T>
T max(T a, T b) {
return a > b ? a : b;
}
// early binding | static binding
class Base {
public:
void show() { std::cout << "\nIn Base \n"; }
};
class Derived : public Base {
public:
void show() { std::cout << "In Derived \n"; }
};
// override
void swap(int* a, int* b){
};
void swap(double* a, double* b){
};
int main() {
int x = 10, y = 20;
double a = 1.2, b = 3.4;
// overloading | as static polymorphism
swap(&x, &y); // swap(int,int)
swap(&a, &b); // swap(double, double)
// parametric polymorphism | as static polymorphism
std::cout << ::max(9, 5) << std::endl;
std::string foo("kingslayer"), bar("widow'swale");
std::cout << ::max(foo, bar) << std::endl;
// early binding or static binding | as static polymorphism
Base* bp = new Derived;
bp->show();
return 0;
}
Output:
9
widow'swale
In Base
A UML diagram can’t directly describe how polymorphism is handled but can be at least partially implemented either statically or dynamically (depending on the static polymorphism dependency of the OOP model).
It is possible to remove the performance impact of indirect calls when the behavior of static polymorphism occurs at compile time rather than run time.
Most importantly, performance is the primary benefit of static polymorphism, and PBD (Policy Based Design) and CRTP (Curiously Recurring Template Pattern) are of its perfect examples which highlight how extremely powerful the technique is.
It provides tons of critically important information to the compiler and optimizer, can check assumptions at compile time, and can select among critical options for the program’s execution.
Hassan is a Software Engineer with a well-developed set of programming skills. He uses his knowledge and writing capabilities to produce interesting-to-read technical articles.
GitHub