Type erasing (TE is this article) is used to decouple the interface (behavior) and the type information in C++. In Python, it’s also called ”duck typing” with dynamic type binding:
Whatever the type of argument is, the only requirement is it contains a walk()
function.
It offers extreme flexibility for programmers, at the expense of runtime overhead for dynamic type binding.
In static type programming language like C++, it is achieved with template and derivation.
Copyright: The following codes are referred from post1 in Zhihu. One can go to the compiler explorer website2 to compile it. Some contents are motivated from this blog3.
Base type to define the behavior
Suppose we want a universal type capable of calling a function without any parameter and returns nothing, i.e., void()
.
We define a pure base type to express the behavior:
Note that any type derived from task_base
is able to invoked by the operator()
once override.
However, this requires all used type should declared as a derived type of task_base
, which is infeasible in most cases (e.g., for native builtin type int
, or type from third party libraries).
Derived type to erase the type info
The ideal use case is we can bind any type (derived or not) to a “universal type”, which provides the interface to execute the behavior:
To achieve this, we should introduce a middle type to “hide” the the type passed in:
The constructor itself is a templated function receiving the actual type as universal reference, and the class template type F
is inferred from the universal type U
.
Meanwhile, it’s also a type derived from the task_base
class to express the expected behavior (via the override operator()
function).
Putting together
We cannot directly use task_model
as it’s still the derived type.
Hence another proxy is needed to completely erase the type:
Now users are able to define a my_task
instance by any “callable” type ( void ()
, i.e., the type is able to be invoked with zero parameter, and returns void
).
my_task
itself is not a derived type, nor contains any virtual function.
If the type passed in is a builtin type or from third party library, we can define a special version of task_model
with partial specialization:
Summary
The user-defined type is not necessary to derive from any class. The pure base class defines the required interfaces, from whom the templated middle class derives from.
The advantages of type erasure are:
- No derivation constraints, user don’t need to define their custom type as the derived class.
- No runtime binding overhead, the resolution and binding behavior happens at compile time via the template instantiation.