COW è basic_string
vietato in C ++ 11 e versioni successive?
per quanto riguarda
" Ho ragione che C ++ 11 non ammette implementazioni basate su COW std::string
?
Sì.
per quanto riguarda
"In caso affermativo, questa restrizione è esplicitamente dichiarata da qualche parte nel nuovo standard (dove)?
Quasi direttamente, in base a requisiti di complessità costante per una serie di operazioni che richiederebbero O ( n ) la copia fisica dei dati della stringa in un'implementazione COW.
Ad esempio, per le funzioni membro
auto operator[](size_type pos) const -> const_reference;
auto operator[](size_type pos) -> reference;
... che in un'implementazione COW ¹ entrambi attivano la copia dei dati della stringa per annullare la condivisione del valore della stringa, lo standard C ++ 11 richiede
C ++ 11 §21.4.5 / 4 :
” Complessità: tempo costante.
... che esclude la copia di tali dati e, quindi, COW.
C ++ 03 supportato implementazioni COW da non avere questi requisiti costante complessità, e, in determinate condizioni restrittive, consentendo chiamate operator[]()
, at()
, begin()
, rbegin()
, end()
, o rend()
per i riferimenti di invalidazione, puntatori e iteratori rinvio alle voci di stringa, cioè addirittura subire una Copia dei dati COW. Questo supporto è stato rimosso in C ++ 11.
COW è vietato anche tramite le regole di invalidazione C ++ 11?
In un'altra risposta che al momento della scrittura è selezionata come soluzione, e che è fortemente votata e quindi apparentemente creduta, si afferma che
” Per una stringa COW, chiamare non- const
operator[]
richiederebbe la creazione di una copia (e l'invalidamento dei riferimenti), cosa non consentita dal paragrafo [tra virgolette] sopra [C ++ 11 §21.4.1 / 6]. Quindi, non è più legale avere una stringa COW in C ++ 11.
Questa affermazione è errata e fuorviante in due modi principali:
- Indica erroneamente che solo le
const
funzioni di accesso non articolo devono attivare una copia dei dati COW.
Ma anche le const
funzioni di accesso agli elementi devono attivare la copia dei dati, perché consentono al codice client di formare riferimenti o puntatori che (in C ++ 11) non è consentito invalidare in seguito tramite le operazioni che possono attivare la copia dei dati COW.
- Si presume erroneamente che la copia dei dati COW possa causare l'annullamento del riferimento.
Ma in una corretta implementazione la copia dei dati COW, l'annullamento della condivisione del valore della stringa, viene eseguita in un punto prima che vi siano riferimenti che possono essere invalidati.
Per vedere come funzionerebbe una corretta implementazione C ++ 11 COW di basic_string
, quando i requisiti O (1) che lo rendono non valido vengono ignorati, pensa a un'implementazione in cui una stringa può passare da una politica di proprietà all'altra. Un'istanza di stringa inizia con la policy Sharable. Con questo criterio attivo non possono esserci riferimenti a elementi esterni. L'istanza può passare alla policy Unique e deve farlo quando viene potenzialmente creato un riferimento a un elemento, ad esempio con una chiamata a .c_str()
(almeno se ciò produce un puntatore al buffer interno). Nel caso generale di più istanze che condividono la proprietà del valore, ciò comporta la copia dei dati della stringa. Dopo la transizione alla policy Unique, l'istanza può tornare a Sharable solo tramite un'operazione che invalida tutti i riferimenti, come l'assegnazione.
Quindi, mentre la conclusione di quella risposta, che le stringhe COW sono escluse, è corretta, il ragionamento offerto è errato e fortemente fuorviante.
Sospetto che la causa di questo malinteso sia una nota non normativa nell'appendice C di C ++ 11:
C ++ 11 §C.2.11 [diff.cpp03.strings], su §21.3:
Modifica : i basic_string
requisiti non consentono più stringhe con conteggio dei riferimenti.
Logica: l' invalidazione è leggermente diversa con le stringhe conteggiate in riferimento. Questa modifica regolarizza il comportamento (sic) per questo standard internazionale.
Effetto sulla funzionalità originale: il codice C ++ 2003 valido può essere eseguito in modo diverso in questo standard internazionale
Qui la logica spiega il motivo principale per cui si è deciso di rimuovere il supporto COW speciale per C ++ 03. Questa logica, il perché , non è il modo in cui lo standard impedisce effettivamente l'implementazione COW. Lo standard non consente COW tramite i requisiti O (1).
In breve, le regole di invalidazione C ++ 11 non escludono un'implementazione COW di std::basic_string
. Ma escludono un'implementazione COW in stile C ++ 03 ragionevolmente efficiente e senza restrizioni come quella in almeno una delle implementazioni della libreria standard di g ++. Lo speciale supporto C ++ 03 COW ha consentito l'efficienza pratica, in particolare utilizzando le const
funzioni di accesso agli oggetti, a costo di regole sottili e complesse per l'invalidazione:
C ++ 03 §21.3 / 5 che include il supporto COW "prima chiamata":
" Riferimenti, puntatori e iteratori che si riferiscono agli elementi di una basic_string
sequenza possono essere invalidati dai seguenti usi di basic_string
quell'oggetto:
- Come argomento per funzioni non membri swap()
(21.3.7.8), operator>>()
(21.3.7.9) e getline()
(21.3. 7.9).
- Come argomento per basic_string::swap()
.
- Chiamata data()
e c_str()
funzioni membro.
- Chiamata non const
funzioni membro, ad eccezione operator[]()
, at()
, begin()
, rbegin()
, end()
, e rend()
.
- A valle di uno degli usi di cui sopra, tranne le forme insert()
e erase()
che iteratori ritorno, la prima chiamata a non const
funzioni membro operator[]()
, at()
, begin()
, rbegin()
,end()
, o rend()
.
Queste regole sono così complesse e sottili che dubito che molti programmatori, se ce ne siano, potrebbero fornire un riassunto preciso. Non potevo.
Cosa succede se i requisiti O (1) vengono ignorati?
Se i requisiti di tempo costante di C ++ 11, ad esempio, operator[]
vengono ignorati, COW per basic_string
potrebbe essere tecnicamente fattibile, ma difficile da implementare.
Le operazioni che potrebbero accedere al contenuto di una stringa senza incorrere nella copia dei dati COW includono:
- Concatenazione tramite
+
.
- Uscita tramite
<<
.
- Utilizzo di un
basic_string
argomento come per le funzioni di libreria standard.
Quest'ultimo perché la libreria standard può fare affidamento su conoscenze e costrutti specifici dell'implementazione.
Inoltre, un'implementazione potrebbe offrire varie funzioni non standard per l'accesso ai contenuti delle stringhe senza attivare la copia dei dati COW.
Un fattore di complicanza principale è che in C ++ 11 l' basic_string
accesso agli elementi deve attivare la copia dei dati (annullando la condivisione dei dati della stringa) ma è necessario non lanciare , ad esempio C ++ 11 §21.4.5 / 3 " Produce: niente.". E quindi non può utilizzare l'allocazione dinamica ordinaria per creare un nuovo buffer per la copia dei dati COW. Un modo per aggirare questo è utilizzare un heap speciale in cui la memoria può essere riservata senza essere effettivamente allocata, quindi riservare la quantità richiesta per ogni riferimento logico a un valore stringa. Riservare e annullare la prenotazione in un tale mucchio può essere tempo costante, O (1), e allocare l'importo che si è già prenotato può esserenoexcept
. Per soddisfare i requisiti dello standard, con questo approccio sembra che dovrebbe esserci uno di questi heap speciali basati sulla prenotazione per allocatore distinto.
Note:
¹ La funzione di accesso const
elemento attiva una copia dei dati COW perché consente al codice client di ottenere un riferimento o un puntatore ai dati, che non è consentito invalidare da una successiva copia dei dati attivata, ad esempio, dalla funzione di accesso non const
elemento.