2013-09-06 17 views
7
vector<int> f(const vector<int>& v) 
{ 
    vector<int> ret(v.size()); 
    fill(ret.begin(), ret.end(), 123); 
    copy(v.begin(), v.end(), ret.begin()); 
    return ret; 
} 

int main() 
{ 
    vector<int> v(10); 
    v = f(v); 
} 

Jeśli optymalizacja wartości zwracanej zostanie zastosowana do f, wówczas zmienna lokalna ret współużytkuje ten sam adres, co v w głównej. Ale jeśli jest to prawdą, wypełnienie ret powoduje wyrzucenie danych w v przed kopiowaniem. Kod jest poprawny bez RVO, a optymalizacje nie powinny naruszać zachowania.Jak to działa z optymalizacją wartości zwracanej?

Czy jest to bezpieczne, czy też nie rozumiem poprawnie instrukcji RVO?

+2

Istnieją dwa różne rodzaje optymalizacji zwrotu: Kopia z 'ret' do tymczasowego, aby powrócić z' f', i kopia z tego tymczasowego do 'v'. Dozwolone jest tutaj tylko pierwsze. – dyp

+0

Możliwe, że kompilator zoptymalizuje całą linię wypełnienia mimo wszystko. – OllieB

+0

@OllieB Być może, ale to był tylko prosty przykład, który wymyśliłem. –

Odpowiedz

5

Co się dzieje, jest to:

Na stronie rozmówcy, gniazdo powrotu jest warunkiem, który może utrzymać wynik, to znaczy, że rozmówca zapewnia pamięćdla zmiennej typu std::vector<int>. Oczekuje wywoływanej metody konstruowania wartości i sam jest odpowiedzialny za wywołanie destruktora, gdy wynik nie jest już używany i zwolnienie pamięci (w razie potrzeby prawdopodobnie po prostu żyje na stosie).

Wywołana funkcja (który może żyć w innej jednostce tłumaczenia!) Byłoby bez NRVO, więc to:

  • Podaj gniazdo pamięci dla ret.
  • Skonstruuj lokalną zmienną ret w tym gnieździe pamięci.
  • Do rzeczy ...
  • Kopiuj - skonstruuj wartość zwracaną w przewidzianym do tego celu przedziale, kopiując ret.
  • Zadzwoń pod nr ret destruktor.

Teraz z NRVO, decyzja, aby zoptymalizować można to zrobić w wywołanej funkcji w jednostce translacyjnej. Przekształca powyższe dane w:

  • Skonstruuj ret w pamięci szczeliny powrotnej metody.
  • Czy rzeczy ...

Nie trzeba robić nic innego jak pamięć jest własnością i destruktor jest nazywany przez rozmówcę i dlatego optymalizacja jest przezroczysty dla rozmówcy :)

To, oczywiście, nie można wyeliminować przypisanie do v w twoim przykładzie. Jeśli przechowujesz wynik w innej zmiennej, np.

std::vector<int> w = f(v); 

NRVO skonstruuje ret bezpośrednio do pamięci w jest (jak to zostanie przekazany jako gniazda zwrotnym f).

+0

RVO nigdy nie może zlikwidować kopii zadania? –

+0

@NeilKirk No. Operacja przypisania jest zawsze wykonywana. Tylko bezpośrednia inicjalizacja zmiennej (która ma w swoim składzie '='), może to zrobić, ale prawdziwego przypisania nie można zoptymalizować za pomocą (N) RVO. –

5

Kod jest poprawny, zrozumienie RVO nie jest - szczególnie to:

the local variable ret shares the same address as v 

To nie podziela adres zmiennej przypisać go do. Technicznie, gdy zwrócisz automatyczną zmienną lokalną, zostanie ona skopiowana do tymczasowej. RVO przeskakuje część.

To idzie tak:

  create variable `ret` 
        | 
copy ret to a temporary before returning 
        | 
     assign the temporary to v 

NRVO (w tym przypadku), by przejść na drugą część.

Kompilatory są inteligentne, więc nie zdziw się, jeśli po prostu zoptymalizują całość, ponieważ w ogóle nie ma zauważalnych zachowań.

0

RVO dotyczy budowy nowych obiektów. Oczywiście w główne, kompilator może przechodzić v jako adres wartości powrotu, ponieważ funkcja skonstruować nowy vector<int> w tym adres i v już zbudowane. Tak więc minie adres tymczasowy. RVO dotyczy tego, co dzieje się w funkcji : bez RVO, kompilator skonstruowałby: ret i , a następnie skopiowałby go w adresie przekazanym jako ukryty argument . W miejscu wywołania, jeśli zwracana wartość jest używana do (kopiowania) skonstruuj obiekt, wówczas kompilator pominie kopię tymczasową obiektu w nowym obiekcie i po prostu przekazuje adres obiektu, który ma zostać skonstruowany, po prostu . Jeśli jednak zwracana wartość to , ale nie jest używana jako argument konstruktora kopiowania, to jednak nie ma zbyt wiele kompilatora. Potrzebujesz obiektu typu poprawnego , więc musi wygenerować tymczasowy.

Powiązane problemy