Just a follow-up on Runtime polymorphism without dynamic memory allocation for the reason it’s C++ and you can do all kinds of weird things. This is a fun article. I didn’t think it through.
Last time, I wanted a clear API for the caller and I created an abstraction above std::variant. I did it by returning a lambda that the caller can simply call with the needed arguments.
Then I thought “What if I can do it even more simple?”. I’d like to have the same API as the implementation with the pointer.
object->function(argument);
But without heap allocations.
The storage remains a variant. And I return a pointer to the currently selected type in the variant.
#include <cassert> #include <variant> struct P { virtual int f(int) const = 0; virtual ~P() = default; }; struct A : P { int f(int in) const override {return in + 1;} }; struct B : P { int f(int in) const override {return in + 2;}; }; struct factory { std::variant<A, B> object; P* create(char o) { switch(o) { case 'a': object = A{}; break; default: object = B{}; break; } return std::visit([](auto& obj) -> P* { return &obj; }, object); } }; int main() { factory f{}; assert(f.create('a')->f(1) == 2); assert(f.create('b')->f(1) == 3); }
A particular thing is that I have to use trailing return type for the lambda visitor. This is because “std::visit requires the visitor to have the same return type for all alternatives of a variant” (Clang). So I return all objects through the base class. I’m back to virtual inheritance functions.
Besides the raw pointer, I’m wondering what could go wrong with this approach.