[c++] È sicuro premere un elemento di push_back dallo stesso vettore?


3 Answers

Sì, è sicuro, e le implementazioni di libreria standard saltano attraverso i cerchi per renderlo tale.

Credo che gli implementatori riportino questo requisito al 23.2 / 11 in qualche modo, ma non riesco a capire come, e non riesco a trovare qualcosa di più concreto. Il meglio che posso trovare è questo articolo:

http://www.drdobbs.com/cpp/copying-container-elements-from-the-c-li/240155771

L'ispezione delle implementazioni di libc ++ e libstdc ++ mostra che sono anche sicuri.

Question
vector<int> v;
v.push_back(1);
v.push_back(v[0]);

Se il secondo push_back causa una riallocazione, il riferimento al primo intero nel vettore non sarà più valido. Quindi questo non è sicuro?

vector<int> v;
v.push_back(1);
v.reserve(v.size() + 1);
v.push_back(v[0]);

Questo lo rende sicuro?




Entrambi sono sicuri poiché push_back copierà il valore, non il riferimento. Se stai memorizzando i puntatori, questo è ancora sicuro per quanto riguarda il vettore, ma sappi solo che avrai due elementi del tuo vettore che puntano agli stessi dati.

Sezione 23.2.1 Requisiti generali del contenitore

16
  • a.push_back (t) Aggiunge una copia di t. Richiede: T deve essere CopyInsertable in X.
  • a.push_back (rv) Aggiunge una copia di rv. Richiede: T deve essere MoveInsertable in X.

Le implementazioni di push_back devono quindi garantire che sia inserita una copia di v[0] . Per contro esempio, supponendo un'implementazione che sarebbe riallocata prima di copiare, non accoderebbe sicuramente una copia di v[0] e come tale viola le specifiche.




Non è ovvio che il primo esempio sia sicuro, perché l'implementazione più semplice di push_back sarebbe quella di riallocare il vettore, se necessario, e quindi copiare il riferimento.

Ma almeno sembra essere sicuro con Visual Studio 2010. La sua implementazione di push_back fa una gestione speciale del caso quando si respinge un elemento nel vettore. Il codice è strutturato come segue:

void push_back(const _Ty& _Val)
    {   // insert element at end
    if (_Inside(_STD addressof(_Val)))
        {   // push back an element
                    ...
        }
    else
        {   // push back a non-element
                    ...
        }
    }



La prima versione NON è sicuramente sicura:

Le operazioni su iteratori ottenuti chiamando un contenitore di libreria standard o una funzione membro della stringa possono accedere al contenitore sottostante, ma non devono modificarlo. [Nota: in particolare, le operazioni di contenitore che invalidano gli iteratori sono in conflitto con le operazioni sugli iteratori associati a quel contenitore. - nota finale]

dalla sezione 17.6.5.9

Si noti che questa è la sezione sulle gare di dati, che le persone normalmente pensano in concomitanza con il threading ... ma la definizione effettiva coinvolge relazioni "accade prima" e non vedo alcuna relazione di ordinamento tra i molteplici effetti collaterali di push_back in gioco qui, vale a dire l'invalidazione di riferimento sembra non essere definito come ordinato rispetto alla copia-costruzione del nuovo elemento di coda.






Related