2010-01-27 30 views
29

Czytałem Copy and Swap.Co to jest copy elision i jak optymalizuje on idiom kopiowania i wymiany?

Próbowałem przeczytać kilka linków na Kopiuj Elision, ale nie mogłem dowiedzieć się, co to właściwie znaczy. Czy ktoś może wyjaśnić, czym jest ta optymalizacja, a zwłaszcza co oznacza następujący tekst:

Nie jest to tylko kwestia wygody, ale w rzeczywistości optymalizacji. Jeśli parametr (y) wiąże się z lwartością (inny niestanowiący ograniczenia obiekt), kopia obiektu jest tworzona automatycznie podczas tworzenia parametru (parametrów). Jednak gdy s wiąże się z wartością rwartową (tymczasowy obiekt, literał), kopia jest zazwyczaj elizowana, co zapisuje wywołanie konstruktora kopiowania i destruktora. We wcześniejszej wersji operatora przypisania, w którym parametr jest akceptowany jako odniesienie stałe, kopiowanie nie występuje, gdy odwołanie jest powiązane z wartością r. Powoduje to powstanie i zniszczenie dodatkowego obiektu.

+0

Powiązane: [Czym jest copy elision?] (Http://stackoverflow.com/questions/12953127/what-are-copy-elision-and-return-value-optimization) –

Odpowiedz

33

Konstruktor kopii istnieje, aby wykonywać kopie. W teorii, kiedy piszesz wiersz jak:

CLASS c(foo()); 

kompilator musiałby wywołać konstruktor kopiujący skopiować powrót foo() do c.

Kopiowanie elision to technika pomijania wywołania konstruktora kopii, aby nie płacić za narzut.

Na przykład kompilator może ustawić, że foo() bezpośrednio skonstruuje jego wartość zwracaną na c.

Oto kolejny przykład. Powiedzmy, że masz funkcję:

void doit(CLASS c); 

Jeśli zadzwonisz go z rzeczywistą argumentu, kompilator musi wywołać konstruktora kopii tak, że oryginalny parametr nie może być modyfikowany:

CLASS c1; 
doit(c1); 

Ale teraz rozważyć inny przykład, powiedzmy, że nazywają swoją funkcję tak:

doit(c1 + c1); 

operator+ będzie musiał utworzyć tymczasowy obiekt (rvalue). Zamiast wywoływania konstruktora kopiowania przed wywołaniem doit(), kompilator może przekazać tymczasowy plik utworzony przez operator+ i przekazać go do doit().

2

Oto przykład:

#include <vector> 
#include <climits> 

class BigCounter { 
public: 
    BigCounter &operator =(BigCounter b) { 
     swap(b); 
     return *this; 
    } 

    BigCounter next() const; 

    void swap(BigCounter &b) { 
     vals_.swap(b); 
    } 

private: 
    typedef ::std::vector<unsigned int> valvec_t; 
    valvec_t vals_; 
}; 

BigCounter BigCounter::next() const 
{ 
    BigCounter newcounter(*this); 
    unsigned int carry = 1; 
    for (valvec_t::iterator i = newcounter.vals_.begin(); 
     carry > 0 && i != newcounter.vals_.end(); 
     ++i) 
    { 
     if (*i <= (UINT_MAX - carry)) { 
     *i += carry; 
     } else { 
     *i += carry; 
     carry = 1; 
     } 
    } 
    if (carry > 0) { 
     newcounter.vals_.push_back(carry); 
    } 
    return newcounter; 
} 

void someFunction() 
{ 
    BigCounter loopcount; 
    while (true) { 
     loopcount = loopcount.next(); 
    } 
} 

W somefunction linijka loopcount = loopcount.next(); korzyści znacznie od kopiowania elizji. Jeśli elizacja kopiowania nie była dozwolona, ​​linia ta wymagałaby 3 wywołań konstruktora kopiowania i powiązanego wywołania destruktora. Przy dozwolonej liczbie kopii można ją ograniczyć do 1 wywołania konstruktora kopiowania, wyraźnego wewnątrz BigCount::next(), gdzie zadeklarowano newcounter.

Jeśli operator = zostały uznane i zdefiniowane tak:

BigCounter &BigCounter::operator =(const BigCounter &b) { 
    BigCounter tmp(b); 
    swap(tmp); 
    return *this; 
} 

nie będzie już musiała być 2 wywołania konstruktora kopii, nawet z kopiowaniem elizji. Jeden do skonstruowania newcounter, a drugi do konstrukcji tmp. Bez funkcji kopiowania nadal będzie 3. Dlatego deklarowanie operator =, tak więc jego argument wymaga wywołania konstruktu kopii, może być optymalizacją, gdy użyjemy idiomu "kopiuj i zamień" dla operatora przypisania. Kiedy konstruktor kopiowania zostanie wywołany do skonstruowania argumentu, jego wywołanie może zostać usunięte, ale jeśli zostanie wywołane w celu utworzenia zmiennej lokalnej, może nie być.