2016-01-04 10 views
16

Rozważmy następujące kodyemplace_back() problem pod VS2013

std::vector<int> nums{21, 22, 23, 24}; 
nums.emplace_back(nums[0]); 
nums.emplace_back(nums[1]); 

for (auto n : nums) { 
    std::cout << n << std::endl; 
} 

wyjściu VS2013

21 
22 
23 
24 
-17891602 
22 

Dlaczego -17891602 jest tutaj?

Wyjście GCC 4.8.4 jest poprawna następująco

21 
22 
23 
24 
21 
22 

Potem porównać realizację emplace_back między VS2013 i GCC

VS2013

template<class... _Valty> 
    void emplace_back(_Valty&&... _Val) 
    { // insert by moving into element at end 
    if (this->_Mylast == this->_Myend) 
     _Reserve(1); 
    _Orphan_range(this->_Mylast, this->_Mylast); 
    this->_Getal().construct(this->_Mylast, 
     _STD forward<_Valty>(_Val)...); 
    ++this->_Mylast; 
    } 

GCC

template<typename _Tp, typename _Alloc> 
template<typename... _Args> 
    void 
    vector<_Tp, _Alloc>:: 
    emplace_back(_Args&&... __args) 
    { 
    if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage) 
     { 
     _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish, 
           std::forward<_Args>(__args)...); 
     ++this->_M_impl._M_finish; 
     } 
    else 
     _M_emplace_back_aux(std::forward<_Args>(__args)...); 
    } 

Wydaje się, że dziwne _Reserve(1); jest używane w VS2013. Czemu?

Edit:

Wartość hex z -17891602 jest 0xFEEEFEEE, co oznacza

używanego przez debugowania HeapFree Microsoftu(), aby zaznaczyć pamięć zwolniona sterty

Patrz magic number

Następnie debugowałem powyższą linię kodów linkiem e i znalazłem wywołanie 0XFEEEFEEE spowodowane przez _Reserve(1);.

+0

Możliwy duplikat: [Czy std :: wektor emplace_back kopiuje konstrukcję z elementu samego wektora?] (Http://stackoverflow.com/questions/24908718/can-stdvector-emplace-back-copy-construct-from -element-of-the-vector-sam w sobie). – cpplearner

+0

W tym pytaniu chcę wiedzieć, dlaczego ''Reserve (1)' jest używane w 'VS2013'? różni się linkiem powyżej. – zangw

Odpowiedz

12

Jest to problem występujący w VS2013 i VS2015 podczas umieszczania elementu w wektorze, który zawiera element. Jeśli rozmiar zmienia się, odniesienie do wprowadzanego elementu jest nieprawidłowe. Praca polega na utworzeniu kopii elementu we wstawce, a następnie wstawieniu tego.

auto n = nums[0]; 
nums.emplace_back(n); 

Wezwanie _Reserve jest tam, aby upewnić się, że jest jakaś pamięć przeznaczona na wektorze (więc nie muszą być sprawdzane w późniejszych operacji).

+3

To obejście dla niefortunnego wyboru projektu. Podobne obejście to '.emplace_back (nums [0] +0)'. Dodanie 0 nie powinno zmienić wyniku, logicznie! – MSalters

+2

@hvd Zobacz dyskusję na http://open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#2164. Obecne brzmienie wydaje się wymagać, aby to działało. –

+1

@ T.C. Dzięki za referencję. To 'nums.emplace_back (std :: move (n));' nie jest wymagane do pracy; Tęskniłem za tym, ponieważ 'emplace_back' przyjmuje odwołanie do przekazywania, które staje się tutaj odwołaniem do lwartości, reguła, która czyni to nieważnym, nie ma zastosowania, nawet jeśli jest to wyraźnie wspomniane w notatce w [res.on.argument] 1.3. Usunąłem mój poprzedni komentarz. Podobno przez długi czas plan miał formalnie uczynić go nieważnym również dla odniesień l-wartościowych, zgodnie z linkiem do # 760 tam. – hvd

2

The emplace issue

obiekty związane z opakowania parametru funkcji o człon emplace nie powinnysię elementy lub pod-obiektów elementów pojemnika.

The emplace_back() nazywa się funkcją emplace() pod VS2013.

template<class... _Valty> 
    iterator emplace(const_iterator _Where, _Valty&&... _Val) 
    { // insert by moving _Val at _Where 
    size_type _Off = _VIPTR(_Where) - this->_Myfirst; 

#if _ITERATOR_DEBUG_LEVEL == 2 
    if (size() < _Off) 
     _DEBUG_ERROR("vector emplace iterator outside range"); 
#endif /* _ITERATOR_DEBUG_LEVEL == 2 */ 

    emplace_back(_STD forward<_Valty>(_Val)...); 
    _STD rotate(begin() + _Off, end() - 1, end()); 
    return (begin() + _Off); 
    } 

znalazłem jeden dobry post, które opisują pewne szczegóły emplace_back() realizacji w ramach VS2013.

std::vector klasa ma różne członków instancji (zarówno regularne jak i wewnętrzne), a wśród nich są następujące:

  • _Myfirst - punkty do początku tablicy danych
  • _Mylast - punkty do pierwszego niezainicjowane elementu w tablicy danych. Jeśli jest równe _Myend, następne wstawienie spowoduje realokację. Masz tego faceta na end() rozmowy
  • _Myend - punkty do końca tablicy danych

Tak, jeśli chodzi o adresy pamięci, następujące nierówności odbywa się:

_Myfirst <=<= _Mylast <=<= _Myend

See ta linia z _Reserve(1) w tym? To wywołanie funkcji powoduje, że nasz błąd się ujawnia.

Przejdźmy przez krok po kroku (patrz poprzednia przykładowa funkcja).

nums.emplace_back(nums[0]); 

Najpierw otrzymujemy odniesienie do elementu ponieważ operator[] zwraca reference

reference operator[](size_type _Pos) 
{ ... } 

Następnie wkraczamy emplace_back metody, przekazując świeże i ważne odniesienie do elementu, który chcemy wstawić . To, co od razu widzimy na początku, to sprawdzenie przekroczenia rozmiaru wektora. Dopóki nasze wstawienie powoduje wzrost rozmiaru wektora, otrzymujemy odwołanie unieważnione zaraz po ponownej alokacji. To jest powód tak interesującego, ale oczekiwanego (po wdrożeniu) zachowania.

+1

To jest proponowana rezolucja, z której LWG najwyraźniej się nie zgadza. Poza tym nie ma to wpływu na 'emplace_back'. –

Powiązane problemy