Allocator specialized for array types in c++14?

落爺英雄遲暮 提交于 2021-02-08 01:23:08

问题


Why isn't there an array template specialization for std::allocator<T[]> in c++14?

When playing around trying to specialize std::allocator<T[]> myself I hit a dead-end when implementing the construct() and destroy() method. Is this the reason? Why then have construct() and destroy() part of std::allocator<>?

template <T>
class allocator <T[]> {

    // ...most is similar for std::allocator<>...

    template <class U, class... Args>
    void construct( U *p, Args&&.. args)
    {
        // what to write for array construction?
        // Could be std::initializer_list<T> or uniform initalizer list.
        // Want to pass on to constructor somehow.
        // ::new ((void *)p) [] U(std::forward<Args>(args)...); 
    }

    template <class U>
    void destroy( U* p )
    {
        // no-op.
    }
};

Thanks for any hints!


回答1:


Warning: None of what follows makes sense. Don't do this at home. Don't mix arrays and dynamic allocation. The following serves merely as a mental exercise in spelling out very long template names.

The standard allocator does indeed not provide for array construction, but you can easily build your own allocator that does. Note that we're free to provide construct()/destroy() mechanisms as we please:

#include <memory>
#include <type_traits>

template <typename T>
struct array_allocator : std::allocator<T>
{
    template <typename C, typename ...Args>
    typename std::enable_if<std::is_array<C>::value>::type construct(C * p, Args &&... args)
    {
        ::new (static_cast<void *>(p)) C { std::forward<Args>(args)... };
    }

    template <typename C, typename ...Args>
    typename std::enable_if<!std::is_array<C>::value>::type construct(C * p, Args &&... args)
    {
        ::new (static_cast<void *>(p)) C(std::forward<Args>(args)...);
    }

    template <typename C, typename ...Args>
    typename std::enable_if<std::is_array<C>::value>::type destroy(C * p)
    {
        using U = typename std::remove_extent<C>::type;
        using UAT = typename std::allocator_traits<array_allocator>::template rebind_traits<U>;
        typename std::allocator_traits<array_allocator>::template rebind_alloc<U> ra(*this);

        for (std::size_t i = 0, e = std::extent<C>::value; i != e; ++i)
        {
            UAT::destroy(ra, std::addressof((*p)[e - i - 1]));
        }
    }

    template <typename C, typename ...Args>
    typename std::enable_if<!std::is_array<C>::value>::type destroy(C * p)
    {
        p->~C();
    }
};

Here is a usage example, allocating and constructing arrays of 13 int.:

using T = int[13];
using TA = array_allocator<T>;
using TAT = std::allocator_traits<TA>;

#include <iostream>

int main()
{
    TA a;
    T * p = TAT::allocate(a, 2);   // allocates space for two T's

    TAT::construct(a, p, 1, 2, 3);
    TAT::construct(a, p + 1, 4, 5, 6);

    for (T * q = p; q != p + 2; ++q)
        for (int * r = *q; r != *q + 13; ++r)
            std::cout << *r << "\n";

    TAT::destroy(a, p + 1);
    TAT::destroy(a, p);

    TAT::deallocate(a, p, 2);
}

As linked in the comments, though, beware that it's impossible to prove that the inherited allocate function returns the correct amount of memory.



来源:https://stackoverflow.com/questions/24542647/allocator-specialized-for-array-types-in-c14

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