할당자가 직접 메모리 수영장을 보유하지 않으려면 (따라서 복사 할 수 있으므로) 할당자가 될 수 있습니까?

StackOverflow https://stackoverflow.com//questions/11703643

  •  13-12-2019
  •  | 
  •  

문제

나는 컨테이너를 쓰고 사용자가 사용자 정의 할당자를 사용하도록 허용하고 싶지만, 참조로 할당자를 통과 해야하는지 또는 값으로 할당자를 통과 해야하는지 알 수 없습니다.

은 할당 자 개체가 이 아닌 을 직접 포함하는 이 아니라고 할당자 객체가 (또는 적어도 적어도, 합리적인 가정)가 보장되며, 할당자를 복사하고 메모리를 기대하는 것이 좋습니다.할당 자의 수영장은 크로스 호환 가능합니까?또는 참조로 할당자를 항상 전달해야합니까?

(i는 앨리어싱에 대한 걱정이 걱정되기 시작 하므로이 가정에 의존 할 수 있는지 여부를 만드는 것이므로 기준은> 2의 성능을 유도합니다.)

도움이 되었습니까?

해결책

C ++ 11.5 섹션 17.6.3.5 할당 자 요구 사항 [allocator.requirements] Conforming Allocators의 요구 사항을 지정합니다. 요구 사항 중 :

X                    an Allocator class for type T
...
a, a1, a2            values of type X&
...
a1 == a2             bool          returns true only if storage
                                   allocated from each can be
                                   deallocated via the other.
                                   operator== shall be reflexive,
                                   symmetric, and transitive, and
                                   shall not exit via an exception.
...
X a1(a);                           Shall not exit via an exception.
                                   post: a1 == a
.

i.e. 할당자를 복사 할 때 두 사본이 서로의 포인터를 삭제할 수 있어야합니다.

상상 가능하게는 내부 버퍼를 할당 자에 넣을 수 있지만, 사본은 다른 버퍼의 목록을 유지해야합니다. 또는 할당자는 포인터가 항상 내부 버퍼 (사용자 또는 다른 복사본에서 또는 다른 사본에서) 내부 버퍼에서 항상 오기 때문에 할당자가 항상 NO-OP가 될 수 있습니다.

그러나 계획이 무엇이든 사본은 "교차 호환"이어야합니다.

업데이트

여기에 "짧은 문자열 최적화"를 수행하는 C ++ 11 Conforming Allocator입니다. C ++ 11을 부합시키기 위해서는 복사본이 동일한 복사기를 할당 자에게 "내부"버퍼를 넣어야했습니다.

#include <cstddef>

template <std::size_t N>
class arena
{
    static const std::size_t alignment = 16;
    alignas(alignment) char buf_[N];
    char* ptr_;

    std::size_t 
    align_up(std::size_t n) {return n + (alignment-1) & ~(alignment-1);}

public:
    arena() : ptr_(buf_) {}
    arena(const arena&) = delete;
    arena& operator=(const arena&) = delete;

    char* allocate(std::size_t n)
    {
        n = align_up(n);
        if (buf_ + N - ptr_ >= n)
        {
            char* r = ptr_;
            ptr_ += n;
            return r;
        }
        return static_cast<char*>(::operator new(n));
    }
    void deallocate(char* p, std::size_t n)
    {
        n = align_up(n);
        if (buf_ <= p && p < buf_ + N)
        {
            if (p + n == ptr_)
                ptr_ = p;
        }
        else
            ::operator delete(p);
    }
};

template <class T, std::size_t N>
class stack_allocator
{
    arena<N>& a_;
public:
    typedef T value_type;

public:
    template <class U> struct rebind {typedef stack_allocator<U, N> other;};

    explicit stack_allocator(arena<N>& a) : a_(a) {}
    template <class U>
        stack_allocator(const stack_allocator<U, N>& a)
            : a_(a.a_) {}
    stack_allocator(const stack_allocator&) = default;
    stack_allocator& operator=(const stack_allocator&) = delete;

    T* allocate(std::size_t n)
    {
        return reinterpret_cast<T*>(a_.allocate(n*sizeof(T)));
    }
    void deallocate(T* p, std::size_t n)
    {
        a_.deallocate(reinterpret_cast<char*>(p), n*sizeof(T));
    }

    template <class T1, std::size_t N1, class U, std::size_t M>
    friend
    bool
    operator==(const stack_allocator<T1, N1>& x, const stack_allocator<U, M>& y);

    template <class U, std::size_t M> friend class stack_allocator;
};

template <class T, std::size_t N, class U, std::size_t M>
bool
operator==(const stack_allocator<T, N>& x, const stack_allocator<U, M>& y)
{
    return N == M && &x.a_ == &y.a_;
}

template <class T, std::size_t N, class U, std::size_t M>
bool
operator!=(const stack_allocator<T, N>& x, const stack_allocator<U, M>& y)
{
    return !(x == y);
}
.

다음과 같이 사용할 수 있습니다 :

#include <vector>

template <class T, std::size_t N> using A = stack_allocator<T, N>;
template <class T, std::size_t N> using Vector = std::vector<T, stack_allocator<T, N>>;

int main()
{
    const std::size_t N = 1024;
    arena<N> a;
    Vector<int, N> v{A<int, N>(a)};
    v.reserve(100);
    for (int i = 0; i < 100; ++i)
        v.push_back(i);
    Vector<int, N> v2 = std::move(v);
    v = v2;
}
.

위의 문제에 대한 모든 할당은 크기가 1KB 인 로컬 arena에서 가져옵니다. 이 할당자를 가치 또는 참조로 통과 할 수 있어야합니다.

다른 팁

표준 호환 할당 자에 대한 요구 사항을 만듭니다. 이러한 요구 사항에는 Alloc<T> a, b가있는 경우 a == b가 있으면 b를 사용하여 a로 할당 된 것들을 할당 할 수 있습니다. 할당자는 근본적으로 stateless


C ++ 11에서는 상태 저장 할당 자에 대한 지원이 이제 훨씬 더 포함되어 있습니다. 객체를 복사하고 이동할 때 할당자가 다른 경우 하나의 컨테이너를 다른 컨테이너에서 복사하거나 이동할 수 있는지 여부와 할당자가 어떻게 복사되거나 이동하는지

에 대한 질문에 대답하기 위해서 : 아니요, 할당 자를 복사하는 것이 의미가 있고 할당자를 복사 할 수 없을 수도 있음을 확실히 not 가정 할 수 있습니다.

여기에 23.2.1 / 7이 주제 :

별도로 지정하지 않는 한이 절에 정의 된 모든 컨테이너는 할당자를 사용하여 메모리를 얻습니다 (17.6.3.5 참조). 이러한 컨테이너 유형에 대한 생성자 복사 첫 번째 매개 변수에서 allocator_traits<allocator_-type>::select_on_container_copy_construction를 호출하여 할당자를 가져옵니다. 이동 생성자 이동중인 컨테이너에 속하는 할당 자의 이동 공사로하여 할당자를 얻습니다. 그러한 할당 자의 이러한 이동 구조는 예외를 통해 종료되지 않아야한다. 이러한 컨테이너 유형의 다른 모든 생성자는 값 유형이 컨테이너의 값 유형과 동일한 할당자를 빌드하는 Allocator& 인수 (17.6.3.5)를 취합니다. [참고 : 생성자의 호출이 선택적 Allocator 인수의 기본값을 사용하는 경우 할당 자 유형은 값 초기화를 지원해야합니다. -End note]이 할당 자의 사본은 각 컨테이너 오브젝트의 수명 동안 또는 할당자를 대체 할 때까지 이들 생성자 및 모든 멤버 함수에 의해 수행되는 모든 메모리 할당에 사용됩니다. 할당자는 과제를 통해서만 대체 될 수 있습니다 교환(). 할당 자 교체는 COPY 할당, 이동 할당 또는 ALLOCATOR의 이동 또는 전환기의 스와핑을 allocator_traits<allocator_type>::propagate_on_container_copy_assignment::value, allocator_traits<allocator_type>::propagate_on_container_move_assignment::value 또는 allocator_traits<allocator_type>::propagate_on_container_swap::value는 해당 컨테이너 작업의 구현 내에서 true입니다. 컨테이너의 스왑 함수에 대한 호출의 동작은 동일하거나 generacodicicetagode를 비교하는 할당자가 사실이 아닌 경우 정의되지 않습니다. 이 절에 정의 된 모든 컨테이너 유형에서 멤버 allocator_traits<allocator_type>::propagate_on_container_swap::value는 컨테이너를 구성하는 데 사용되는 할당 자의 사본을 반환하거나 해당 할당자를 대체 한 경우 가장 최근의 대체품의 사본을 반환합니다.

get_allocator()

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top