2011-10-28 11 views
14

Powiedzmy, że mam dwa wektory i przenoszę jeden do drugiego, v1 = std::move(v2); czy po tym stanie jeszcze być w stanie użytecznym?Czy przenoszenie pozostawia obiekt w stanie używalności?

+1

To wygląda na duplikat http://stackoverflow.com/q/7027523/576911. Zobacz tę odpowiedź: http://stackoverflow.com/questions/7027523/what-can-i-do-with-a-moved-from-object/7028318#7028318 na to pytanie, które omawia dozwolone operacje pod względem warunków wstępnych. –

Odpowiedz

22

Z n3290, 17.6.5.15 przemieszczające się od stanu rodzajów bibliotek [lib.types.movedfrom]

  1. Przedmioty typów określonych w C++ średnia biblioteki mogą być przemieszczane z (12.8) . Operacje przenoszenia mogą być jawnie określone lub niejawnie wygenerowane. O ile nie określono inaczej, takie przeniesione obiekty należy umieścić w ważnym, lecz nieokreślonym stanie.

Ponieważ stan jest prawidłowy, to znaczy można bezpiecznie operować v2 (na przykład poprzez przypisanie do niego, co byłoby umieścić go z powrotem do znanego stanu). Ponieważ nie jest to określone, oznacza to, że nie można na przykład polegać na żadnej konkretnej wartości dla v2.empty(), o ile jest w tym stanie (ale wywołanie go nie spowoduje awarii programu).

Należy zauważyć, że ten aksjomat semantyki ruchu ("przeniesiony z obiektów pozostawia się w poprawnym, ale nieokreślonym stanie") jest czymś, do czego cały kod powinien dążyć (przez większość czasu), a nie tylko komponentami biblioteki standardowej. Podobnie jak semantyka konstruktorów kopiowania, powinna ona być kopiowana, ale nie jest wymuszona.

+0

+1, ale sposób w jaki jest napisany, wygląda na to, że nie wiesz, czy 'v2' jest pusty po' v2 = std :: vector {}; ' – Gabriel

+0

@Gabriel Dobra sprawa, edycja jest w porządku. –

7

Nie, pozostaje w bliżej nieokreślonym stanie.

Fragment open-std-org artykułu -

.. ruch() daje jej docelowa wartość swojego argumentu, ale nie jest zobowiązany do zachowania wartości źródła. Tak więc, dla wektora, move() można rozsądnie oczekiwać, że pozostawi swój argument jako wektor o zerowej wydajności, aby uniknąć konieczności kopiowania wszystkich elementów. Innymi słowy, ruch jest potencjalnie destrukcyjnym odczytem.

+7

Zostaje w nieokreślonym, ale prawidłowym stanie. Oznacza to, że nadal można używać obiektu w sposób, który ma tylko warunek wstępny, że obiekt jest ważny. Na przykład możesz wywołać wektor :: clear() na tym przeniesionym z wektora, aby uzyskać go w znanym stanie, a następnie rozpocząć wstawianie do niego obiektów. – bames53

+3

To __must__ będzie ważne. W przeciwnym razie, co by się stało, gdy obiekt wykracza poza zakres.Najprawdopodobniej nie będzie niczego w swoim magazynie danych, ale cokolwiek to jest, będzie ważne. – Damon

+1

Istnieje wiele (abstrakcyjnych) poziomów ważności. Jeśli chodzi o język, obiekt po przeniesieniu jest ważny, o ile obiekt nadal istnieje, jego destruktor można wywołać bez powodowania niezdefiniowanych zachowań itp. Jednak umowa obiektu może zostać zmieniona z powodu przeniesienia . Na przykład fakt, że wektor stał się wielkością zerową (nie posiadającą pojemności) jest prawidłowym wektorem C++, ale obiektem posiadającym metodę, która ustawia konkretny element na wektorze, bez sprawdzania (teraz unieważnionego) założenia, że wektor powinien mieć określony rozmiar, staje się ... – rwong

0

Jeśli chcesz używać v2 po przeprowadzce, będziemy chcieli, aby zrobić coś takiego:

v1 = std::move(v2); 
v2.clear(); 

W tym momencie v1 będzie miał oryginalną zawartość v2 i v2 będzie dobrze -definiowany pusty stan. Działa to na wszystkich kontenerach STL (i łańcuchach, jeśli o to chodzi), a jeśli implementujesz własne klasy obsługujące semantykę ruchu, prawdopodobnie będziesz chciał zrobić coś podobnego.

Jeśli twoja konkretna implementacja STL rzeczywiście pozostawia obiekt w stanie pustym, to drugie wyczyszczenie() będzie zasadniczo operacją nieopublikowaną. W rzeczywistości, jeśli tak jest, byłoby prawną optymalizacją dla kompilatora, aby wyeliminować funkcję clear() po ruchu.