Co mogę zrobić z przeniesionym obiektem?

138

Czy norma precyzyjnie określa, co mogę zrobić z przedmiotem po jego przeniesieniu? Kiedyś myślałem, że wszystko, co można zrobić z przeniesionym obiektem, to go zniszczyć, ale to nie wystarczy.

Na przykład weź szablon funkcji swapzdefiniowany w bibliotece standardowej:

template <typename T>
void swap(T& a, T& b)
{
    T c = std::move(a); // line 1
    a = std::move(b);   // line 2: assignment to moved-from object!
    b = std::move(c);   // line 3: assignment to moved-from object!
}

Oczywiście musi istnieć możliwość przypisania do obiektów przeniesionych, w przeciwnym razie linie 2 i 3 nie powiodą się. Więc co jeszcze mogę zrobić z obiektami przeniesionymi? Gdzie dokładnie mogę znaleźć te szczegóły w standardzie?

(Nawiasem mówiąc, dlaczego T c = std::move(a);zamiast T c(std::move(a));w wierszu 1?)

fredoverflow
źródło

Odpowiedzi:

53

Obiekty przeniesione z istnieją w nieokreślonym, ale prawidłowym stanie. Sugeruje to, że chociaż obiekt może nie być już w stanie zrobić wiele, wszystkie jego funkcje składowe powinny nadal wykazywać określone zachowanie - w tym operator=- i wszystkie jego elementy w określonym stanie - i nadal wymaga zniszczenia. Norma nie podaje konkretnych definicji, ponieważ byłaby unikalna dla każdego UDT, ale możesz znaleźć specyfikacje dla typów Standardów. Niektóre, takie jak pojemniki, są stosunkowo oczywiste - po prostu przenoszą swoją zawartość, a pusty pojemnik to dobrze zdefiniowany prawidłowy stan. Prymitywy nie modyfikują przeniesionego obiektu.

Uwaga boczna: Uważam, że jest T c = std::move(a)tak, że jeśli konstruktor przenoszenia (lub konstruktor kopiujący, jeśli nie podano żadnego ruchu), jest jawny, funkcja zakończy się niepowodzeniem.

Szczeniak
źródło
26
Nie wszystkie jego funkcje członkowskie będą wykazywać określone zachowanie. Tylko te bez warunków wstępnych. Na przykład prawdopodobnie nie chcesz pop_backprzenieść się z vector. Ale z pewnością możesz się dowiedzieć, czy tak jest empty().
Howard Hinnant,
6
@Howard Hinnant: pop_backfrom a empty vectorma i tak niezdefiniowane zachowanie, z pamięci, więc jestem prawie pewien, że pop_backz przeniesionego wektora wykazującego niezdefiniowane zachowanie jest spójne.
Puppy
12
Rozmawiamy o obiektach przeniesionych. Brak obiektów, o których wiadomo, że są w stanie pustym. Obiekty przeniesione z obiektu mają nieokreślony stan (o ile oczywiście nie określono inaczej). [lib.types.movedfrom]
Howard Hinnant,
5
@Howard Nieokreślony, ale ważny, więc pop_backnadal zachowuje się jak na każdym prawidłowym wektorze (może to być nawet pusty wektor).
Christian Rau
1
co w tym kontekście oznacza nieokreślone i ważne?
Ankur S
114

17.6.5.15 [lib.types.movedfrom]

Obiekty typów zdefiniowanych w standardowej bibliotece C ++ można przenieść z (12.8). Operacje przenoszenia mogą być jawnie określone lub niejawnie generowane. O ile nie określono inaczej, takie przeniesione przedmioty należy umieścić w stanie ważnym, ale nieokreślonym.

Gdy obiekt jest w nieokreślonym stanie, możesz wykonać na nim dowolną operację, która nie ma warunków wstępnych. Jeśli istnieje operacja z warunkami wstępnymi, które chcesz wykonać, nie możesz wykonać tej operacji bezpośrednio, ponieważ nie wiesz, czy nieokreślony stan obiektu spełnia warunki wstępne.

Przykłady operacji, które generalnie nie mają warunków wstępnych:

  • zniszczenie
  • zadanie
  • const obserwatorzy takie jak get, empty,size

Przykłady operacji, które zazwyczaj mają warunki wstępne:

  • dereferencja
  • pop_back

Ta odpowiedź jest teraz wyświetlana w formacie wideo tutaj: http://www.youtube.com/watch?v=vLinb2fgkHk&t=47m10s

Howard Hinnant
źródło
1
Ale mógłbym po prostu sprawdzić warunki wstępne, tak jak w przypadku każdego innego obiektu, prawda?
fredoverflow
6
@FredOverflow O ile same te sprawdzenia nie mają oczywiście żadnych warunków wstępnych.
Christian Rau
1
@Chris: Ale czym to się różni od normalnego, nieprzenoszonego obiektu?
fredoverflow
2
Może to być osobne pytanie, ale czy to oznacza: jeśli mam ciąg z elementami członkowskimi char* buffer;i int length;, to mój konstruktor / przypisanie przenoszenia musi zamienić (lub ustawić) wartość obu? Lub byłoby OK, jeśli długość była nieokreślona (co oznacza, że emptyi sizezwracania wartości bez znaczenia)?
UncleBens
3
@ 6502: Nie masz sensu. Klasa C ++ 03 nie „narusza standardu C ++ 0x”, ponieważ wygenerowany znacznik ruchu naruszyłby standard. A kod C ++ 03 nie przenosiłby tej klasy, więc nie ma powodu, aby generować znacznik ruchu.
MSalters