In C++ 11 Abschnitt 17.6.3.5 Zuweisungsanforderungen [allocator.requirements] gibt die Anforderungen für konforme Zuweiser an. Zu den Anforderungen gehören:
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. Wenn Sie einen Zuordner kopieren, müssen die zwei Kopien in der Lage sein, die Zeiger des anderen zu löschen.
Vorstellbar könnte man interne Puffer in Zuweiser setzen, aber Kopien müssten eine Liste der anderen Puffer behalten. Oder vielleicht könnte ein Allokator eine Invariante haben, dass die Deallokation immer ein No-Op ist, weil der Zeiger immer von einem internen Puffer kommt (entweder von Ihrem eigenen oder von einer anderen Kopie).
Aber was auch immer das Schema, Kopien müssen "Cross-kompatibel" sein.
aktualisieren
ist hier ein allocator C++ 11 entspricht, welche die "kurzen String-Optimierung" der Fall ist. Um es C++ 11 kompatibel machen, musste ich setze die „internen“ den Allocator externe Puffer, so dass Kopien sind gleich:
#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);
}
Es könnte wie folgt verwendet werden:
#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;
}
All Allokationen für das obige Problem werden von der lokalen arena
gezogen, die 1 KB groß ist. Sie sollten in der Lage sein, diesen Allokator nach Wert oder Verweis zu übergeben.
Können Sie bitte einige Einzelheiten hinzufügen, was Sie zu tun versuchen? Sie müssen natürlich mit Stateful Allocators vorsichtig sein, aber bestimmte Standardoperationen wie das Verschieben eines Containers aus einem anderen Container haben Standard-Idiome. –
@KerrekSB: Ah, ok. Zum Beispiel versuche ich, eine "resize" - oder "reserve" -Operationsoperation als "copy-this-container-and-swap" -Operation zu implementieren. Dies funktioniert, wenn der Zuordner keinen eigenen Speicherpool hat. (Wenn ja, könnte ich zweimal tauschen, aber das Erstellen einer Kopie könnte immer noch teuer sein und könnte den Stapel überfluten, wenn es einen eigenen Pool enthält.) – Mehrdad