| Start |
|---|
A move-only std::function
This causes problems in a few contexts, like lambdas storing unique ptrs. If you are using the In particular, Thus the To make this library-worthy, you'd want to add in a small buffer optimization, so it does not store every callable on the heap. Adding SBO would require a non-default live example (opens new window) of the above code (with no SBO). |
| Erasing down to a Regular type with manual vtable C++ thrives on what is known as a Regular type (or at least Pseudo-Regular). A Regular type is a type that can be constructed and assigned-to and assigned-from via copy or move, can be destroyed, and can be compared equal-to. It can also be constructed from no arguments. Finally, it also has support for a few other operations that are highly useful in various This is the root paper (opens new window), but in C++11 would want to add I will use the manual vtable approach to type erasure here. live example (opens new window). Such a regular type can be used as a key for a would be basically a map from anothing regular, to anything copyable. Unlike Small object optimization requires that we store an aligned storage buffer within the I would start at |
| Basic mechanism Type erasure is a way to hide the type of an object from code using it, even though it is not derived from a common base class. In doing so, it provides a bridge between the worlds of static polymorphism (templates; at the place of use, the exact type must be known at compile time, but it need not be declared to conform to an interface at definition) and dynamic polymorphism (inheritance and virtual functions; at the place of use, the exact type need not be known at compile time, but must be declared to conform to an interface at definition). The following code shows the basic mechanism of type erasure. At the use site, only the above definition need to be visible, just as with base classes with virtual functions. For example: Note that this is not a template, but a normal function that only needs to be declared in a header file, and can be defined in an implementation file (unlike templates, whose definition must be visible at the place of use). At the definition of the concrete type, nothing needs to be known about We can now pass an object of this class to the function defined above: |
| Erasing down to a contiguous buffer of T Not all type erasure involves virtual inheritance, allocations, placement new, or even function pointers. What makes type erasure type erasure is that it describes a (set of) behavior(s), and takes any type that supports that behavior and wraps it up. All information that isn't in that set of behaviors is "forgotten" or "erased". An an It can take a In this case, the data we can extract from the thing we are erasing, together with our "view" non-owning state, means we don't have to allocate memory or write custom type-dependent functions. Live example (opens new window). An improvement would be to use a non-member |
| Type erasing type erasure with std::any This example uses C++14 and The syntax we end up with is: which is almost optimal. This example is based off of work by @dyp (opens new window) and @cpplearner (opens new window) as well as my own. First we use a tag to pass around types: This trait class gets the signature stored with an This creates a function pointer type, and a factory for said function pointers, given an
We want to be able to type-erase more than one method at a time. So we bundle them up in a tuple, and write a helper wrapper to stick the tuple into static storage on a per-type basis and maintain a pointer to them. We could specialize this for a cases where the vtable is small (for example, 1 item), and use direct pointers stored in-class in those cases for efficiency. Now we start the This searches the methods that the super any supports for SFINAE and better error messages: Next we create the or in C++17: Note that using a non-lambda can make things hairy, as we use the type for a lookup step. This can be fixed, but would make this example longer than it already is. So always initialize an any method from a lambda, or from a type parametarized on a lambda. A factory method, not needed in C++17 I believe: This is the augmented Because we store the Test code: live example (opens new window). Originally posted here (opens new window) in a SO self question & answer (and people noted above helped with the implementation). |
Read Article
https://develop.horje.com/learn/1434/reference