Is het veilig om een ​​element van dezelfde vector terug te duwen?

Als de tweede push_back een hertoewijzing veroorzaakt, is de verwijzing naar het eerste gehele getal in de vector niet langer geldig. Dus dit is niet veilig?

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

Dit maakt het veilig?


Antwoord 1, autoriteit 100%

Het lijkt op http://www.open-std. org/jtc1/sc22/wg21/docs/lwg-closed.html#526heeft dit probleem (of iets dat er erg op lijkt) aangepakt als een mogelijk defect in de standaard:

1) Parameters genomen door const-referentie kunnen tijdens de uitvoering worden gewijzigd
van de functie

Voorbeelden:

Gegeven std::vector v:

v.insert(v.begin(), v[2]);

v[2] kan worden gewijzigd door elementen van vector te verplaatsen

De voorgestelde oplossing was dat dit geen defect was:

vector::insert(iter, value) is vereist om te werken omdat de standaard
geeft geen toestemming om het niet te laten werken.


Antwoord 2, autoriteit 65%

Ja, het is veilig, en standaard bibliotheekimplementaties springen door hoepels om het zo te maken.

Ik geloof dat uitvoerders deze vereiste op de een of andere manier terugvoeren naar 23.2/11, maar ik kan niet achterhalen hoe, en ik kan ook niets concreets vinden. Het beste dat ik kan vinden is dit artikel:

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

Inspectie van de implementaties van libc++ en libstdc++ toont aan dat ze ook veilig zijn.


Antwoord 3, autoriteit 38%

De standaard garandeert dat zelfs je eerste exemplaar veilig is. C++11 citeren

[reeks.reqmts]

3 In tabellen 100 en 101 … geeft Xeen sequentiecontainerklasse aan, ageeft een waarde van Xaan die elementen van het type bevat T, … Tstaat voor een lvalue of een const rvalue van X::value_type

16 Tabel 101 …

Expressiea.push_back(t)RetourtypevoidOperationele semantiekVoegt een kopie van t.Vereist:Tmoet CopyInsertablezijn in X. Containerbasic_string, deque, list, vector

Dus ook al is het niet bepaald triviaal, de implementatie moet garanderen dat het de referentie niet ongeldig maakt bij het uitvoeren van de push_back.


Antwoord 4, autoriteit 21%

Het is niet duidelijk dat het eerste voorbeeld veilig is, omdat de eenvoudigste implementatie van push_backzou zijn om eerst de vector opnieuw toe te wijzen, indien nodig, en dan de referentie te kopiëren.

Maar het lijkt in ieder geval veilig te zijn met Visual Studio 2010. De implementatie van push_backzorgt voor een speciale behandeling van het geval wanneer je een element in de vector terugduwt.
De code is als volgt opgebouwd:

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
                    ...
        }
    }

Antwoord 5, autoriteit 9%

Dit is geen garantie van de standaard, maar als een ander datapunt is v.push_back(v[0])veilig voor libc++ van LLVM.

libc++’s std::vector::push_backroept __push_back_slow_pathaan wanneer het geheugen opnieuw moet toewijzen:

void __push_back_slow_path(_Up& __x) {
  allocator_type& __a = this->__alloc();
  __split_buffer<value_type, allocator_type&> __v(__recommend(size() + 1), 
                                                  size(), 
                                                  __a);
  // Note that we construct a copy of __x before deallocating
  // the existing storage or moving existing elements.
  __alloc_traits::construct(__a, 
                            _VSTD::__to_raw_pointer(__v.__end_), 
                            _VSTD::forward<_Up>(__x));
  __v.__end_++;
  // Moving existing elements happens here:
  __swap_out_circular_buffer(__v);
  // When __v goes out of scope, __x will be invalid.
}

Antwoord 6, autoriteit 3%

De eerste versie is absoluut NIET veilig:

Bewerkingen op iterators verkregen door het aanroepen van een standaard bibliotheekcontainer of tekenreekslidfunctie kunnen toegang krijgen tot de onderliggende container, maar mogen deze niet wijzigen. [ Opmerking: In het bijzonder containerbewerkingen die iterators ongeldig maken, conflicteren met bewerkingen op iterators die aan die container zijn gekoppeld.— eindnoot ]

uit sectie 17.6.5.9


Merk op dat dit de sectie over dataraces is, waar mensen normaal gesproken aan denken in combinatie met threading… effecten van push_backdie hier in het spel zijn, namelijk de ongeldigverklaring van de referentie lijkt niet te zijn gedefinieerd als geordend met betrekking tot het kopiëren van het nieuwe staartelement.


Antwoord 7

Het is volkomen veilig.

In uw tweede voorbeeld heeft u

v.reserve(v.size() + 1);

wat niet nodig is, want als de vector te groot wordt, impliceert dit de reserve.

Vector is verantwoordelijk voor dit spul, niet jij.


Antwoord 8

Beide zijn veilig omdat push_back de waarde kopieert, niet de referentie. Als je pointers opslaat, is dat nog steeds veilig voor wat betreft de vector, maar weet dat je twee elementen van je vector hebt die naar dezelfde gegevens verwijzen.

Sectie 23.2.1 Algemene vereisten voor containers

16

  • a.push_back(t) Voegt een kopie van t toe. Vereist: T moet CopyInsertable in X zijn.
  • a.push_back(rv) Voegt een kopie van rv toe. Vereist: T moet MoveInsertable in X zijn.

Implementaties van push_back moeten er daarom voor zorgen dat een kopie vanv[0]wordt ingevoegd. Als tegenvoorbeeld, aannemende dat een implementatie opnieuw zou worden toegewezen voordat het werd gekopieerd, zou het niet zeker een kopie van v[0]toevoegen en als zodanig de specificaties schenden.


Antwoord 9

Vanaf 23.3.6.5/1: Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid.

Omdat we aan het einde invoegen, worden verwijzingen niet ongeldig gemaakt alsde grootte van de vector niet wordt gewijzigd. Dus als de capacity() > size()dan werkt het gegarandeerd, anders is het gegarandeerd ongedefinieerd gedrag.

Other episodes