How to make my uninitialised_allocator safe?

怎甘沉沦 提交于 2019-11-29 11:09:28

Fwiw, I think the design can be simplified, assuming a C++11 conforming container:

template <class T>
class no_init_allocator
{
public:
    typedef T value_type;

    no_init_allocator() noexcept {}
    template <class U>
        no_init_allocator(const no_init_allocator<U>&) noexcept {}
    T* allocate(std::size_t n)
        {return static_cast<T*>(::operator new(n * sizeof(T)));}
    void deallocate(T* p, std::size_t) noexcept
        {::operator delete(static_cast<void*>(p));}
    template <class U>
        void construct(U*) noexcept
        {
            static_assert(std::is_trivially_default_constructible<U>::value,
            "This allocator can only be used with trivally default constructible types");
        }
    template <class U, class A0, class... Args>
        void construct(U* up, A0&& a0, Args&&... args) noexcept
        {
            ::new(up) U(std::forward<A0>(a0), std::forward<Args>(args)...);
        }
};
  1. I see little advantage to deriving from another allocator.

  2. Now you can let allocator_traits handle rebind.

  3. Template the construct members on U. This helps if you want to use this allocator with some container that needs to allocate something other than a T (e.g. std::list).

  4. Move the static_assert test into the single construct member where it is important.

You can still create a using:

template <class T>
using uninitialised_vector = std::vector<T, no_init_allocator<T>>;

And this still fails to compile:

unitialised_vector< std::vector<int> > x(10);


test.cpp:447:17: error: static_assert failed "This allocator can only be used with trivally default constructible types"
                static_assert(std::is_trivially_default_constructible<U>::value,
                ^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I think the test for is_trivially_destructible is overkill, unless you also optimize destroy to do nothing. But I see no motivation in doing that since I believe it should get optimized anyway whenever appropriate. Without such a restriction you can:

class A
{
    int data_;
public:
    A() = default;
    A(int d) : data_(d) {}
};

int main()
{
    uninitialised_vector<A> v(10);
}

And it just works. But if you make ~A() non trivial:

    ~A() {std::cout << "~A(" << data_ << ")\n";}

Then, at least on my system, you get an error on construction:

test.cpp:447:17: error: static_assert failed "This allocator can only be used with trivally default constructible types"
                static_assert(std::is_trivially_default_constructible<U>::value,
                ^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I.e. A is no longer trivially constructible if it has a non-trivial destructor.

However even with the non-trivial destructor you can still:

    uninitialised_vector<A> v;
    v.push_back(A());

This works, only because I didn't overreach with requiring a trivial destructor. And when executing this I get ~A() to run as expected:

~A(0)
~A(0)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!