I have a bunch of classes which all inherit the same attributes from a common base class. The base class implements some virtual functions that work in general cases, whilst eac
I have 2 solutions. A simpler one that doesn't preserve the memory address, and one that does preserve the memory address.
Both require that you provide provide downcasts from Base to Derived which isn't a problem in your case.
struct Base {
int a;
Base(int a) : a{a} {};
virtual ~Base() = default;
virtual auto foo() -> void { cout << "Base " << a << endl; }
};
struct D1 : Base {
using Base::Base;
D1(Base b) : Base{b.a} {};
auto foo() -> void override { cout << "D1 " << a << endl; }
};
struct D2 : Base {
using Base::Base;
D2(Base b) : Base{b.a} {};
auto foo() -> void override { cout << "D2 " << a << endl; }
};
For the former one you can create a smart pointer that can seemingly change the held data between Derived (and base) classes:
template struct Morpher {
std::unique_ptr obj;
template auto morph() {
obj = std::make_unique(*obj);
}
auto operator->() -> B* { return obj.get(); }
};
int main() {
Morpher m{std::make_unique(24)};
m->foo(); // D1 24
m.morph();
m->foo(); // D2 24
}
The magic is in
m.morph();
which changes the held object preserving the data members (actually uses the cast ctor).
If you need to preserve the memory location, you can adapt the above to use a buffer and placement new instead of unique_ptr
. It is a little more work a whole lot more attention to pay to, but it gives you exactly what you need:
template struct Morpher {
std::aligned_storage_t buffer_;
B *obj_;
template
Morpher(const D &new_obj)
: obj_{new (&buffer_) D{new_obj}} {
static_assert(std::is_base_of::value && sizeof(D) == sizeof(B) &&
alignof(D) == alignof(B));
}
Morpher(const Morpher &) = delete;
auto operator=(const Morpher &) = delete;
~Morpher() { obj_->~B(); }
template auto morph() {
static_assert(std::is_base_of::value && sizeof(D) == sizeof(B) &&
alignof(D) == alignof(B));
obj_->~B();
obj_ = new (&buffer_) D{*obj_};
}
auto operator-> () -> B * { return obj_; }
};
int main() {
Morpher m{D1{24}};
m->foo(); // D1 24
m.morph();
m->foo(); // D2 24
m.morph ();
m->foo(); // Base 24
}
This is of course the absolute bare bone. You can add move ctor, dereference operator etc.