#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";
}