How to make my uninitialised_allocator safe?

后端 未结 1 1626
粉色の甜心
粉色の甜心 2020-12-19 17:55

Following from this question, I want to use an unitialised_allocator with, say, std::vector to avoid default initialisation of elements upon constr

相关标签:
1条回答
  • 2020-12-19 18:36

    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)
    
    0 讨论(0)
提交回复
热议问题