Here's some code... just doing the obvious things. I don't use C++11's new union
features, which might actually be a more structured way to ensure appropriate alignment and size and clean up the code.
#include <iostream>
template <size_t A, size_t B>
struct max
{
static const size_t value = A > B ? A : B;
};
class X
{
public:
X(char x) { construct(x); }
X(const X& rhs)
{ rhs.interface().copy_construct_at_address(this); }
~X() { interface().~Interface(); }
X& operator=(const X& rhs)
{
// warning - not exception safe
interface().~Interface();
rhs.interface().copy_construct_at_address(this);
return *this;
}
struct Interface
{
virtual ~Interface() { }
virtual void f(int) = 0;
virtual void copy_construct_at_address(void*) const = 0;
};
Interface& interface()
{ return reinterpret_cast<Interface&>(data_); }
const Interface& interface() const
{ return reinterpret_cast<const Interface&>(data_); }
// for convenience use of virtual members...
void f(int x) { interface().f(x); }
private:
void construct(char x)
{
if (x == 'A') new (data_) Impl_A();
else if (x == 'B') new (data_) Impl_B();
}
struct Impl_A : Interface
{
Impl_A() : n_(10) { std::cout << "Impl_A(this " << this << ")\n"; }
~Impl_A() { std::cout << "~Impl_A(this " << this << ")\n"; }
void f(int x)
{ std::cout << "Impl_A::f(x " << x << ") n_ " << n_;
n_ += x / 3;
std::cout << " -> " << n_ << '\n'; }
void copy_construct_at_address(void* p) const { new (p) Impl_A(*this); }
int n_;
};
struct Impl_B : Interface
{
Impl_B() : n_(20) { std::cout << "Impl_B(this " << this << ")\n"; }
~Impl_B() { std::cout << "~Impl_B(this " << this << ")\n"; }
void f(int x)
{ std::cout << "Impl_B::f(x " << x << ") n_ " << n_;
n_ += x / 3.0;
std::cout << " -> " << n_ << '\n'; }
void copy_construct_at_address(void* p) const { new (p) Impl_B(*this); }
double n_;
};
union
{
double align_;
char data_[max<sizeof Impl_A, sizeof Impl_B>::value];
};
};
int main()
{
{
X a('A');
a.f(5);
X b('B');
b.f(5);
X x2(b);
x2.f(6);
x2 = a;
x2.f(7);
}
}
Output (with my comments):
Impl_A(this 0018FF24)
Impl_A::f(x 5) n_ 10 -> 11
Impl_B(this 0018FF04)
Impl_B::f(x 5) n_ 20 -> 21.6667
Impl_B::f(x 6) n_ 21.6667 -> 23.6667
~Impl_B(this 0018FF14) // x2 = a morphs type
Impl_A::f(x 7) n_ 11 -> 13 // x2 value 11 copied per a's above
~Impl_A(this 0018FF14)
~Impl_B(this 0018FF04)
~Impl_A(this 0018FF24)