#include <cassert>
#include <new>
#include <utility>
#include <tuple>
#include <typeinfo>
class holder_base {
public:
virtual void holder_copy_construct(void* dst) const { assert(false); };
virtual void holder_move_construct(void* dst) noexcept { assert(false); };
virtual void holder_copy_assign(holder_base& dst) const { assert(false); };
virtual void holder_move_assign(holder_base& dst) noexcept { assert(false); };
virtual void holder_swap(holder_base& other) noexcept { assert(false); };
virtual ~holder_base() noexcept {};
};
template <typename T, typename U, std::enable_if_t<
std::is_convertible<int std::decay_t<U>::*, int std::decay_t<T>::*>{}
|| std::is_convertible<std::remove_reference_t<U>*, std::remove_reference_t<T>*>{}, int> = 0>
T fast_cast(U&& v) noexcept { return static_cast<T>(std::forward<U>(v)); }
template <typename T, typename U, std::enable_if_t<
!std::is_convertible<int std::decay_t<U>::*, int std::decay_t<T>::*>{}
&& !std::is_convertible<std::remove_reference_t<U>*, std::remove_reference_t<T>*>{}, int> = 0>
T fast_cast(U&& v) noexcept { return dynamic_cast<T>(std::forward<U>(v)); }
template <typename T, bool B = std::is_base_of<holder_base, T>{}>
struct holder_helper : T {
template <typename U>
holder_helper(U&& v) : T{std::forward<U>(v)} {}
};
template <typename T>
struct holder_helper<T, false> : holder_base, T {
template <typename U>
holder_helper(U&& v) : holder_base{}, T{std::forward<U>(v)} {}
};
template <typename T>
class holder : public holder_helper<T> {
public:
template <typename U>
explicit holder(U&& v) noexcept(noexcept(std::decay_t<U>{std::forward<U>(v)}))
: holder_helper<T>{std::forward<U>(v)}
{}
private:
virtual void holder_copy_construct(void* dst) const final override {
new(dst) holder{static_cast<const T&>(*this)};
}
virtual void holder_move_construct(void* dst) noexcept final override {
new(dst) holder{static_cast<T&&>(*this)};
}
template <typename U, std::enable_if_t<std::is_nothrow_copy_constructible<U>{}, int> = 0>
static void holder_copy_assign(holder_base& dst, const U& src) noexcept {
dst.~holder_base();
src.holder_copy_construct(&dst);
}
template <typename U, std::enable_if_t<!std::is_nothrow_copy_constructible<U>{}, int> = 0>
static void holder_copy_assign(holder_base& dst, const U& src) {
T tmp{static_cast<const T&>(src)};
dst.~holder_base();
new(static_cast<void*>(&dst)) holder{std::move(tmp)};
}
virtual void holder_copy_assign(holder_base& dst) const final override {
if (typeid(dst) == typeid(holder))
static_cast<T&>(fast_cast<holder&>(dst)) = static_cast<const T&>(*this);
else
holder_copy_assign(dst, *this);
}
virtual void holder_move_assign(holder_base& dst) noexcept final override {
if (typeid(dst) == typeid(holder))
static_cast<T&>(fast_cast<holder&>(dst)) = static_cast<T&&>(*this);
else {
dst.~holder_base();
holder_move_construct(&dst);
}
}
virtual void holder_swap(holder_base& other) noexcept final override {
if (typeid(other) == typeid(holder)) {
using std::swap;
swap(static_cast<T&>(fast_cast<holder&>(other)), static_cast<T&>(*this));
} else {
T tmp{static_cast<T&&>(*this)};
this->~holder();
other.holder_move_construct(this);
other.~holder_base();
new(static_cast<void*>(&other)) holder{std::move(tmp)};
}
}
};
template <typename T, std::size_t max_size = sizeof(holder<T>), std::size_t alignment = alignof(holder<T>)>
class polymorph {
static_assert(std::is_polymorphic<T>{}, "Base must be polymorphic");
static_assert(std::has_virtual_destructor<T>{}, "Base must have virtual destructor");
static_assert(max_size >= sizeof(holder<T>), "specified size is unsufficient");
static_assert(alignment >= alignof(holder<T>), "specified alignment not strict enough");
static_assert(max_size % alignment == 0, "specified size and alignment inconsistent");
public:
template <typename U>
polymorph(U&& src) noexcept(noexcept(std::decay_t<U>{std::forward<U>(src)})) {
using src_type = std::decay_t<U>;
static_assert(std::is_convertible<src_type*, T*>{}, "Base must be unambigous public base of Argument");
assert(typeid(src_type) == typeid(src)); // argument needs to be of the most derived type
static_assert(!std::is_final<src_type>{}, "argument type must not be final");
static_assert(sizeof(holder<src_type>) <= max_size, "argument size too big");
static_assert(alignof(holder<src_type>) <= alignment, "argument alignment too strict");
static_assert(std::is_nothrow_move_constructible<src_type>{}, "argument must be nothrow move constructible");
static_assert(std::is_nothrow_move_assignable<src_type>{}, "argument must be nothrow move assignable");
static_assert(std::is_nothrow_destructible<src_type>{}, "argument must be nothrow destructible");
// static_assert(std::is_nothrow_swappable_v<src_type>);
auto p = new(static_cast<void*>(data)) holder<src_type>{std::forward<U>(src)};
assert(static_cast<holder_base*>(p) == static_cast<void*>(p)); // sane layout
}
polymorph(const polymorph& other) { other.holder_get().holder_copy_construct(data); }
polymorph(polymorph&& other) noexcept { other.holder_get().holder_move_construct(data); }
polymorph& operator=(const polymorph& rhs) { rhs.holder_get().holder_copy_assign(holder_get()); return *this; }
polymorph& operator=(polymorph&& rhs) noexcept { rhs.holder_get().holder_move_assign(holder_get()); return *this; }
~polymorph() noexcept { this->holder_get().~holder_base(); }
void swap(polymorph& other) noexcept { holder_get().holder_swap(other.holder_get()); }
void swap(polymorph&& other) noexcept { swap(other); }
T& get() noexcept { return fast_cast<T&>(holder_get()); }
const T& get() const noexcept { return fast_cast<const T&>(holder_get()); }
private:
holder_base& holder_get() noexcept { return reinterpret_cast<holder_base&>(data); }
const holder_base& holder_get() const noexcept { return reinterpret_cast<const holder_base&>(data); }
alignas(alignment) char data[max_size];
};
template <typename T, std::size_t S, std::size_t A> void swap(polymorph<T, S, A>& x, polymorph<T, S, A>& y) noexcept { return x.swap(y); }
template <typename T, std::size_t S, std::size_t A> void swap(polymorph<T, S, A>&& x, polymorph<T, S, A>& y) noexcept { return x.swap(y); }
template <typename T, std::size_t S, std::size_t A> void swap(polymorph<T, S, A>& x, polymorph<T, S, A>&& y) noexcept { return x.swap(y); }
template <typename T, std::size_t S, std::size_t A> void swap(polymorph<T, S, A>&& x, polymorph<T, S, A>&& y) noexcept { return x.swap(y); }
////////////////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <vector>
struct base : holder_base {
virtual void foo() = 0;
virtual ~base() {}
};
template <int I>
struct derived : base
{
derived() { std::cout << (void*)this << " derived<" << I << ">::derived()\n"; }
derived(const derived&) { std::cout << (void*)this << " derived<" << I << ">::derived(const derived&)\n"; }
derived(derived&&) noexcept { std::cout << (void*)this << " derived<" << I << ">::derived(derived&&)\n"; }
derived& operator=(const derived&) { std::cout << (void*)this << " derived<" << I << ">& derived<" << I << ">::operator=(const derived&)\n"; return *this; }
derived& operator=(derived&&) noexcept { std::cout << (void*)this << " derived<" << I << ">& derived<" << I << ">::operator=(derived&&)\n"; return *this; }
~derived() noexcept { std::cout << (void*)this << " derived<" << I << ">::~derived()\n"; }
virtual void foo() override { std::cout << (void*)this << " derived<" << I << ">::foo()\n"; }
};
int main() {
std::vector<polymorph<base>> v;
std::cout << "v.emplace_back(derived<0>{});\n";
v.emplace_back(derived<0>{});
std::cout << "v.push_back(derived<1>{});\n";
v.push_back(derived<1>{});
std::cout << "v.emplace_back(derived<2>{});\n";
v.emplace_back(derived<2>{});
std::cout << "***\n";
for ( auto& x : v )
x.get().foo();
std::cout << "v[0] = std::move(v[0]);\n";
v[0] = std::move(v[0]);
std::cout << "v[0] = std::move(v[2]);\n";
v[0] = std::move(v[2]);
std::cout << "v[0] = v[0];\n";
v[0] = v[0];
std::cout << "v[0] = v[1];\n";
v[0] = v[1];
std::cout << "swap(v[0],v[0]);\n";
swap(v[0],v[0]);
std::cout << "swap(v[0],v[2]);\n";
swap(v[0],v[2]);
std::cout << "***\n";
for ( auto& x : v )
x.get().foo();
std::cout << "return 0;\n";
}