2017-02-11 26 views
0

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

#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(); 
} 

Однако только в Debug режим в 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 

компиляции и выполнения работы по назначению в режиме выпуска, хотя.

Вот 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(); 
     } 
    }; 
} 

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

+3

Почему бы вам вставить все это в 'имен std'? – DeiDei

+1

Это выглядит как двусмысленность между попыткой использовать либо конструктор с запрограммированным шаблоном напрямую, либо с помощью конструктора копии по умолчанию для создания нового экземпляра 'StackAllocator' для построения копии. Сообщение об ошибке предлагает компилятору, пытающемуся сделать именно это. Почему ошибка компиляции возникает только в режиме отладки, кто знает. Вам необходимо устранить неоднозначность шаблона, используя SFINAE, только в том случае, если 'T' и' T2' отличаются. ... и это кроме того, что использование 'namespace std' для объявления это ужасно неправильно. –

+0

Я добавил перегрузку конструктора копирования с 'T' в качестве параметра типа вместо' T2', но он все еще не компилируется. Я не совсем понимаю, в чем проблема. –

ответ

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 так путем добавления этих определений типов вы обеспечиваете ваш аллокатор может работать только с подмножеством распределителей , Если вы думаете, что нужно им, просто определить их точно так же std::allocator делает:

typedef value_type& reference; 
    typedef const value_type& const_reference;