#include <cstddef>
#include <cstdlib>

#include <iostream>
#include <vector>
#include <functional>
#include <type_traits>

template <typename Type, bool Selector>
struct default_creator_impl;

template <typename Type, bool Selector>
struct default_deleter_impl;

// for non-POD types
template <typename Type>
struct default_creator_impl<Type, false>
{
    Type* operator()()
    {
        Type* object = static_cast<Type*>(std::malloc(sizeof(Type)));
        return new (object) Type;
    }
};

template <typename Type>
struct default_deleter_impl<Type, false>
{
    void operator()(void* object)
    {
        reinterpret_cast<Type*>(object)->Type::~Type();
        free(object);
    }
};

// for simple types
template <typename Type>
struct default_creator_impl<Type, true>
{
    Type* operator()()
    {
        return static_cast<Type*>(std::malloc(sizeof(Type)));
    }
};

template <typename Type>
struct default_deleter_impl<Type, true>
{
    void operator()(void* object)
    {
        free(object);
    }
};

template <typename Type>
using default_creator = default_creator_impl<Type, std::has_trivial_default_constructor<Type>::value>;

template <typename Type>
using default_deleter = default_deleter_impl<Type, std::has_trivial_default_constructor<Type>::value>;

class element_t
{
    std::function<void(void*)> deleter;
    void* memory;

public:
    element_t() : memory(nullptr) { }
    element_t(element_t&&) = default;
    element_t(const element_t&) = delete;
    ~element_t() { if (deleter) deleter(memory); }

    template <typename Type>
    operator Type&()
    {
        if (memory == nullptr)
        {
            memory = default_creator<Type>()();
            deleter = default_deleter<Type>();
        }

        return *reinterpret_cast<Type*>(memory);
    }
};

class Foo
{
public:
    using size_type = std::size_t;

private:
    static size_type maximum_size;
    
    std::vector<element_t> elements;

public:
    Foo() = default;

    template <typename Value>
    Value& get()
    {
        static const auto index = []() -> size_type
        {
            auto current_index = maximum_size;
            ++maximum_size;

            return current_index;
        }();

        if (elements.size() <= index)
        {
            elements.resize(index + 1);
        }

        return elements[index];
    }

    static size_type get_maximum_size()
    {
        return maximum_size;
    }

    size_type get_current_size() const
    {
        return elements.size();
    }
};

Foo::size_type Foo::maximum_size = 0;

struct Haha
{
    Haha() { std::cout << "create" << std::endl; }
    ~Haha() { std::cout << "destroy" << std::endl; }
};

int main()
{
    {
        Foo a;
        a.get<Haha>();
    }
}
