La mia domanda qui è fondamentalmente un follow-up a:Gli allocatori di stato C++ 11 sono intercambiabili tra i limiti dei tipi?
How can I write a stateful allocator in C++11, given requirements on copy construction?
In sostanza, nonostante il fatto che lo standard C++ 11 consente ora per allocatori stateful, abbiamo ancora il requisito che se si copia un certo Allocator
, la copia deve essere paragonabile tramite l'operatore ==
con l'originale. Ciò indica che la copia può tranquillamente deallocare la memoria assegnata dall'originale e viceversa.
Quindi, a monte, questo impedisce già a un allocatore di mantenere uno stato interno univoco, come un allocatore di slab o un pool di memoria o qualcosa del genere. Una soluzione sarebbe utilizzare un idioma da puntatore a implementazione shared_ptr
per lo stato interno, in modo che tutte le copie di alcuni originali Allocator
utilizzino lo stesso pool di memoria sottostante. Non è male. Tranne ...
Secondo la domanda di cui sopra si fa riferimento, così come la risposta accettata, lo standard anche sembra richiedere che Allocator<T>
ha un costruttore di copia interoperabile con Allocator<U>
, in modo che:
Allocator<T> alloc1;
Allocator<U> alloc2(alloc1);
assert(alloc1 == alloc2); // must hold true
Quindi, in altre parole, i tipi di allocatore devono essere interoperabile indipendentemente dai diversi parametri del modello. Ciò significa che se alloco memoria utilizzando Allocator<T>
, devo essere in grado di deallocare quella memoria utilizzando un'istanza Allocator<U>
costruita dall'originale Allocator<T>
.
... e che è praticamente uno show-stopper per qualsiasi tentativo di scrivere un allocatore che utilizza una sorta di pool di memoria size-based, come una piscina simple_segregated_storage che restituisce solo pezzi di una certa dimensione in base a sizeof(T)
.
Ma ... è proprio vero?
Mi rendo conto che per il numero Allocator<T>::rebind
è richiesto il costruttore di copia interoperabile, pertanto gli utenti dei contenitori non devono conoscere i dettagli interni, ad esempio un tipo di nodo dell'elenco di collegamenti o qualcosa del genere. Ma per quanto posso vedere, il standard itself non sembra dire nulla di così draconiano come il requisito che un costruito da un Allocator<T>
deve essere uguale all'istanza originale Allocator<T>
.
La norma prevede essenzialmente le seguenti semantica, dove X è un tipo Allocator<T>
, a1 e a2 sono esempi di X, Y è un tipo Allocator<U>
e b è un'istanza di Allocator<U>
.
Da: § 17.6.3.5 (requisiti Allocator)
a1 == a2 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.
a1 != a2 : same as !(a1 == a2)
a == b : same as a == Y::rebind<T>::other(b)
a != b : same as !(a == b)
X a1(a); Shall not exit via an exception. post: a1 == a
X a(b); Shall not exit via an exception. post: Y(a) == b, a == X(b)
Quindi, il modo in cui ho letto questo, le istanze di Allocator<T>
costruite da Allocator<U>
non sono necessariamente intercambiabili. La norma richiede semplicemente che a == b
deve essere equivalente al Y(a) == b
, non che a == b
deve essere vero!
Penso che il requisito per il costruttore di copia del confine di tipo incrociato rende questa confusione. Ma, il modo in cui ho letto questo, se ho un Allocator<T>
, deve avere un costruttore di copia che prende un Allocator<U>
, ma che non implica che:
Allocator<T> alloc1;
Allocator<U> alloc2(alloc1);
assert(alloc2 == alloc1);
In altre parole, il modo in cui ho letto questo, l'asserzione di cui sopra è permesso fallire. Ma io non sono sicuro nella mia comprensione qui, perché:
Il accepted answer to this question dica il contrario, e chi ha risposto è un ragazzo con 108K reputazione
L'interazione tra le esigenze costruttore di copia e le esigenze di uguaglianza nella lo standard è un po 'confuso e potrei fraintendere la verbosità.
Quindi, io sono corretto qui? (Per inciso, l'attuazione di boost::pool_allocator
sembra implicare ho ragione, assumendo lo sviluppatore spinta si preoccupa per la conformità agli standard, dal momento che questo allocatore non è intercambiabile tipo attraversato confini.)
"Ciò significa che se assegnare qualche memoria utilizzando Allocatore, devo poter deallocare quella memoria utilizzando un allocatore un'istanza costruito dalla Allocatore originale ". Non penso che significhi questo. Leggi [la risposta di Casey] (http://stackoverflow.com/a/24289614/103167) che spiega che dovresti 'rebindare' 'Allocator ' a 'T' prima di deallocare oggetti di tipo' T'. –
[allocator.requirements]/9 dice "Un allocatore può vincolare i tipi su cui può essere istanziato" il che implica che è possibile vincolarlo a 'sizeof (T) == some_fixed_size'. – dyp
"In altre parole ... la suddetta asserzione può fallire." Questo non aiuta troppo perché 'X (Y (a)) == a' deve ancora contenere. Ciò significa che qualsiasi allocatore creato da 'a' deve avere lo stesso stato' a'. –