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.