Пользовательский распределитель компилируется только в режиме Release в VS 2015

Я написал простой фиктивный распределитель для vector<> так что я могу использовать vector<> как обертка для массивов стека, вот так:

#include <vector>
#include "stdio.h"
#include "stack_allocator.h"

using namespace std;

int main() {
    int buffer[100];
    vector<int, StackAllocator<int>> v((StackAllocator<int>(buffer, 100)));
    v.push_back(2);
    printf("%d", v[0]);
    v.pop_back();
}

Однако только в режиме отладки в VS2015 я получаю следующую ошибку компилятора:

'std::StackAllocator<T2, std::allocator<T>>::StackAllocator(std::StackAllocator<T, std::allocator<T>> &&)':
   cannot convert argument 1 from
      'std::_Wrap_alloc<std::StackAllocator<int,std::allocator<T>>>'
   to
      'const std::allocator<T>&'
in "c:\program files (x86)\microsoft visual studio 14.0\vc\include\xmemory0" at line 952

Компиляция и выполнение работают, как и предполагалось, в режиме Release.

Вот stack_allocator.h:

#pragma once

#include <functional>

namespace std {
    template <typename T, typename Allocator = allocator<T>>
    class StackAllocator {
    public:
        typedef typename allocator_traits<Allocator>::value_type value_type;
        typedef typename allocator_traits<Allocator>::pointer pointer;
        typedef typename allocator_traits<Allocator>::const_pointer const_pointer;
        typedef typename allocator_traits<Allocator>::size_type size_type;
        typedef typename allocator_traits<Allocator>::difference_type difference_type;
        typedef typename allocator_traits<Allocator>::const_void_pointer const_void_pointer;
        typedef typename Allocator::reference reference;
        typedef typename Allocator::const_reference const_reference;

        template<typename T2>
        struct rebind {
            typedef StackAllocator<T2> other;
        };

    private:
        size_t m_size;
        Allocator m_allocator;
        pointer m_begin;
        pointer m_end;
        pointer m_stack_pointer;

        bool pointer_to_internal_buffer(const_pointer p) const {
            return (!(less<const_pointer>()(p, m_begin)) && (less<const_pointer>()(p, m_end)));
        }

    public:
        StackAllocator(const Allocator& alloc = Allocator()) noexcept :
            m_size(0),
            m_allocator(alloc),
            m_begin(nullptr),
            m_end(nullptr),
            m_stack_pointer(nullptr) {
        }

        StackAllocator(pointer buffer, size_t size, const Allocator& alloc = Allocator()) noexcept :
            m_size(size),
            m_allocator(alloc),
            m_begin(buffer),
            m_end(buffer + size),
            m_stack_pointer(buffer) {
        }

        template <typename T2>
        StackAllocator(const StackAllocator<T2, Allocator>& other) noexcept :
            m_size(other.m_size),
            m_allocator(other.m_allocator),
            m_begin(other.m_begin),
            m_end(other.m_end),
            m_stack_pointer(other.m_stack_pointer) {
        }

        pointer allocate(size_type n, const_void_pointer hint = const_void_pointer()) {
            if (n <= size_type(distance(m_stack_pointer, m_end))) {
                pointer result = m_stack_pointer;
                m_stack_pointer += n;
                return result;
            }
            else
                return m_allocator.allocate(n, hint);
        }

        void deallocate(pointer p, size_type n) {
            if (pointer_to_internal_buffer(p))
                m_stack_pointer -= n;
            else
                m_allocator.deallocate(p, n);
        }

        size_type capacity() const noexcept {
            return m_size;
        }

        size_type max_size() const noexcept {
            return m_size;
        }

        pointer address(reference x) const noexcept {
            if (pointer_to_internal_buffer(addressof(x)))
                return addressof(x);
            else
                return m_allocator.address(x);
        }

        const_pointer address(const_reference x) const noexcept {
            if (pointer_to_internal_buffer(addressof(x)))
                return addressof(x);
            else
                return m_allocator.address(x);
        }

        pointer buffer() const noexcept {
            return m_begin;
        }

        template <typename T2, typename... Args>
        void construct(T2* p, Args&&... args) {
            m_allocator.construct(p, forward<Args>(args)...);
        }

        template <typename T2>
        void destroy(T2* p) {
            m_allocator.destroy(p);
        }

        template <typename T2>
        bool operator==(const StackAllocator<T2, Allocator>& other) const noexcept {
            return buffer() == other.buffer();
        }

        template <typename T2>
        bool operator!=(const StackAllocator<T2, Allocator>& other) const noexcept {
            return buffer() != other.buffer();
        }
    };
}

Кто-нибудь знает, почему эта ошибка происходит? Как мне это решить?

1 ответ

Решение

Ваш rebind сломан, должно быть:

template<typename T2>
    struct rebind {
        using Alloc2
          = typename allocator_traits<Allocator>::rebind_alloc<T2>;
        using other = StackAllocator<T2, Alloc2>;
    };

В противном случае повторное связывание всегда создает что-то, используя std::allocator<T2> не то, что связано с текущим Allocator аргумент.

например, если вы создаете экземпляр StackAllocator<int, SomeAlloc<int> а затем привязать его к long ты получаешь StackAllocator<long, std::allocator<long>> это совершенно другой тип.

Я думаю, что режим отладки VC++ создает какой-то тип распределителя оболочки, связывая ваш, и он терпит неудачу из-за вашей поломки rebind,

Кроме того, эти строки являются проблемой:

    typedef typename Allocator::reference reference;
    typedef typename Allocator::const_reference const_reference;

Типы, отвечающие требованиям распределителя, не должны иметь reference а также const_reference поэтому, добавляя эти typedefs, вы гарантируете, что ваш распределитель может работать только с подмножеством распределителей. Если вы думаете, что они вам нужны, просто определите их таким же образом std::allocator делает:

    typedef value_type& reference;
    typedef const value_type& const_reference;
Другие вопросы по тегам