2017-11-11 6 views
0

스택에 작은 컨테이너를 할당하는 데 사용할 수있는 간단한 스택 할당자를 만들었습니다. 나는이 수업을 한동안 사용 해왔다. 그러나 오늘 (_ITERATOR_DEBUG_LEVEL=2)에 디버그 이터레이터를 전환했으며 갑자기이 스위치로 활성화 된 디버깅 코드에서 액세스 위반이 발생했습니다.MSVC 디버그 이터레이터를 활성화 할 때 스택 할당 자 액세스 위반

템플릿 프로그래밍과 표준 라이브러리 코딩 규칙을 혼합하면 디버깅이 매우 어려워지고 있으며 할당 자에 대해서도 전문가가 아닙니다. 할당 자에 대한 일종의 규칙을 위반하고 있습니까?

아래 코드는 오류를 재현 할 수 있어야합니다.

#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)을 사용 중입니다.

+3

표준은 할당 자'a'에서 복사 구성 자'b'가'a'와 동등 할 것을 요구합니다; 이는 'a'에 의해 할당 된 메모리가 'b'에 의해 할당 해제 될 수 있음을 의미합니다. 클래스가이 요구 사항을 위반하므로 유효한 할당자가 아닙니다. 실제로'std :: allocator :: operator =='를 상속합니다. 항상'true'를 반환합니다; 클래스의 인스턴스는 서로 교환 할 수 없습니다. –

+1

원본 답변이 downvoted되었지만, 주된 점은 상태 비 저장 값과 유사한 유형이므로 저장소가 할당 자에있을 수 없다는 것입니다. 같은 유형의 여러 할당자를 어떻게 지원할 지조차 알지 못합니다. 또한 할당량이 아직 사용중인 상태에서 스토리지가 범위를 벗어나지 않도록 보장하기 위해 노력해야합니다. 예 : 할당 취소를 주장하면 저장 영역이 파기되기 전에 호출됩니다. –

답변

0

사실 문제는 @Igor이 말한 요구 사항을 위반 한 것이 었습니다. 할당 자 외부에서 스택 데이터를 저장하여이 문제를 해결했습니다. 이제 StackVector는 데이터의 소유자이며 할당 자와 그 복사본은 모두 해당 메모리를 가리키며 한 할당 자에 의해 할당 된 메모리가 그 할당 자의 사본에 의해 할당 해제 될 수 있습니다.

이 솔루션은 사용자에게 약간의 복잡성을 가하지 만 (이제는 1 대신 2 개의 객체를 관리해야 함) 할당 자 요구 사항에 머무르는 동안 다른 방법은없는 것처럼 보이며, 컨테이너뿐만 아니라.

이 솔루션의 가장 좋은 예는 Chromium's StackAllocator입니다.