启用MSVC调试迭代器时,堆栈分配器访问冲突

时间:2017-11-11 01:58:18

标签: c++ visual-c++ allocator

我已经制作了一个简单的堆栈分配器,可用于在堆栈上分配小容器。 我已经使用这个课程一段时间了,它一直很好。 但是,今天我重新调试了调试迭代器(_ITERATOR_DEBUG_LEVEL=2),我突然在此交换机激活的调试代码中遇到访问冲突。

模板编程和标准库编码约定的混合使得调试非常困难,而且我也不是分配器的专家。我是否违反了分配器的某种规则?

以下代码应该能够重现错误。

#include <memory>
#include <vector>

template <typename T, size_t N, template <typename> typename Allocator = std::allocator>
class StackAllocator : public Allocator<T>
{
    public:
    using base         = Allocator<T>;
    using pointer_type = typename base::pointer;
    using size_type    = typename base::size_type;

    StackAllocator() noexcept = default;
    StackAllocator(const StackAllocator& a_StackAllocator) noexcept = default;

    template <typename U>
    StackAllocator(const StackAllocator<U, N, Allocator>& a_StackAllocator) noexcept
    {
    }

    pointer_type allocate(size_type a_Size, void* a_Hint = nullptr)
    {
        if (!m_Active && a_Size <= N)
        {
            m_Active = true;
            return GetStackPointer();
        }
        else
        {
            return base::allocate(a_Size, a_Hint);
        }
    }

    void deallocate(pointer_type a_Pointer, size_type a_Size)
    {
        if (a_Pointer == GetStackPointer())
        {
            m_Active = false;
        }
        else
        {
            base::deallocate(a_Pointer, a_Size);
        }
    }

    template <class U>
    struct rebind
    {
        using other = StackAllocator<U, N, Allocator>;
    };

    private:
    pointer_type GetStackPointer()
    {
        return reinterpret_cast<pointer_type>(m_Data);
    }

    std::aligned_storage_t<sizeof(T), alignof(T)> m_Data[N];
    bool m_Active = false;
};

template <typename T, size_t N>
class StackVector : public std::vector<T, StackAllocator<T, N>>
{
    public:
    using allocator_type = StackAllocator<T, N>;
    using base           = std::vector<T, allocator_type>;

    StackVector() noexcept(noexcept(allocator_type())):
        StackVector(allocator_type())
    {
    }

    explicit StackVector(const allocator_type& a_Allocator) noexcept(noexcept(base(a_Allocator))):
        base(a_Allocator)
    {
        base::reserve(N);
    }

    using base::vector;
};

int main(int argc, char* argv[])
{
    StackVector<size_t, 1> v;

    return v.capacity();
}

我正在使用MSVC 2017(15.4.0)。

1 个答案:

答案 0 :(得分:0)

问题确实违反了@Igor所说的要求。我已经通过将堆栈数据存储在分配器之外来解决了这个问题。现在,StackVector是数据的所有者,分配器及其副本都指向该内存,确保一个分配器分配的内存可以被该分配器的副本解除分配。

这个解决方案确实会给用户带来一些复杂性(现在必须管理2个对象而不是1个),但是在保持分配器要求的同时似乎没有其他方法,并且可以通过包装容器来避免同样。

此解决方案的一个很好的例子可以在Chromium's StackAllocator中找到。