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 functieVoorbeelden:
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
X
een sequentiecontainerklasse aan,a
geeft een waarde vanX
aan die elementen van het type bevatT
, …T
staat voor een lvalue of een const rvalue vanX::value_type
16 Tabel 101 …
Expressie
a.push_back(t)
Retourtypevoid
Operationele semantiekVoegt een kopie vant.
Vereist:T
moetCopyInsertable
zijn inX
. 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_back
zou 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_back
zorgt 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_back
roept __push_back_slow_path
aan 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_back
die 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.