2014-12-16 29 views
6

Wiem, że w C++ 11 semantyka ruchu została zaimplementowana w kontenerach STL w celu uniknięcia tymczasowych obiektów. A ludzie mówią, że teraz jest idealny do pisania funkcji, które zwracają wartość. Ale mam pewne nieporozumienia związane z ziemią, jak wiele razy unika się kopiowania. Zobacz poniższy przykład:Kontenery STL przenoszą semantykę i zwracają wartość: ile razy unika się kopiowania?

vector<int> myVector() { 
    vector<int> res; 
    res.push_back(4); 
    res.push_back(5); 
    return res; 
} 

vector<int> v = myVector(); 

moim rozumieniu jest to, że w C++ 03, myVector zwraca kopię res (4, 5 kopiowane raz), przy ocenie vector<int> v = myVector();vector<int> „s kopia konstruktora vector<int>(const vector<int> &) jest wywoływany (4 , 5 skopiowany dwukrotnie). Jednak w C++ 11 z semantyką ruchu, chcę się dowiedzieć, która kopia 4 i 5 zostanie ominięta? obie? Czy jest również wywoływana optymalizacja wartości zwracanej w celu skrócenia jednego czasu kopiowania: 4 i 5?

+2

W C++ 03, to dwie kopie, obie podlegają elizacji. W C++ 11 są to dwa ruchy, oba podlegające elizacji. –

+0

Dlaczego są dwa ruchy? – Allanqunzi

+0

Z tego samego powodu mogą istnieć dwie kopie w C++ 03 - przejście od 'res' do wartości zwracanej, a następnie przejście od tego do' v'. –

Odpowiedz

4

Istnieją dwie kopie w C++ 03 i dwa ruchy w C++ 11.

W C++ 03 i C++ 11 kopie/ruchy podlegają elizacji i jako takie (w tym przykładzie) prawdopodobnie nie dojdzie do kopiowania/przenoszenia.

vector<int> myVector() { 
    vector<int> res; 
    res.push_back(4); 
    res.push_back(5); 
    return res;// <- Here we construct the return value with res 
} 

// here we construct v with the return value of myVector: 
vector<int> v = myVector(); 

elizja z res do wartości zwracanej myVector jest dość kruche. W C++ 11, move, które może wystąpić, jeśli elizacja zakończy się niepowodzeniem, będzie prawie tak samo darmowa (jak nie robienie niczego) - 3 kopie wskaźnika i 3 wskaźnik zostanie wyczyszczony. W języku C++ 03 kopia, która może wystąpić w przypadku niepowodzenia, może być kosztowna (przydział pamięci i kopie O (n)).

Elision to nazwa kompilatorów operacji, które można wykonać w pewnych okolicznościach, gdy dwie wartości są przekształcane w jedną, nawet jeśli transformacja spowodowałaby zmianę zachowania (tj. Nie powiedzie się test "jak gdyby").

Gdy tak się dzieje, łączna wartość ma żywotność związku dwóch okresów życia.

W powyższym przykładzie zmienna lokalna res i wartość zwracana myVector() może zostać zmieniona przez regułę NRVO (zwaną optymalizacją wartości zwracanej). Wartość zwracana myVector może zostać zmieniona na v, ponieważ jest to oświadczenie o postaci A a = b, gdzie b jest wyrażeniem, które powoduje anonimową wartość (nazwa takich anonimowych wartości zmieniła się z C++ 03 na C++ 11, więc Nie użyję go), w szczególności wartość zwracana myVector().

Powoduje to, że okres istnienia resv i wartość zwracana myVector() zostaną scalone w jedno życie. W praktyce to, co się dzieje, to: res jest umieszczane bezpośrednio w miejscu, do którego ma trafić wartość zwrotna myVector() (zgodnie z konwencją wywołującą), a więc jest v, a zniszczenie ma tę połączoną wartość pomijaną, aż v wykracza poza zakres, jest konstrukcją w oświadczeniu return i v = myVector().

Lub innymi słowy, v jest skonstruowany bezpośrednio w myVector.

+0

Więc żaden destruktor nie zostanie wywołany, dopóki v nie zostanie zniszczony? – Allanqunzi

+1

@ user2345484 maybe. Elision jest dozwolona przez standard, nie jest gwarantowana. (W praktyce można przewidzieć, kiedy to nastąpi). Jeśli występują oba przypadki wymuszenia, tak, istnieje tylko jedna wartość z jednym okresem życia i nie jest ona niszczona, dopóki 'v' nie wykracza poza zakres. Teraz 'v' może zostać zmieniona na KOLEJNĄ zmienną, w takim przypadku zniszczenie będzie nawet później. – Yakk