2010-01-27 10 views
29

Estaba leyendo Copy and Swap.¿Qué es elisión de copia y cómo se optimiza el modismo de copiar y cambiar?

Intenté leer algunos enlaces en Copy Elision pero no pude entender correctamente lo que significaba. ¿Puede alguien explicar por favor cuál es esta optimización, y especialmente cuál es malo con el texto siguiente

Esto no es sólo una cuestión de conveniencia pero de hecho una optimización. Si el parámetro (s) se une a un lvalue (otro objeto no const), se crea automáticamente una copia del objeto al crear el (los) parámetro (s). Sin embargo, cuando s se une a un valor r (objeto temporal, literal), la copia generalmente se elimina, lo que guarda una llamada a un constructor de copia y un destructor. En la versión anterior del operador de asignación, donde el parámetro se acepta como referencia constante, la elisión de copia no ocurre cuando la referencia se une a un valor r. Esto resulta en un objeto adicional que se crea y destruye.

+0

Relacionado: [What is copy elision?] (Http://stackoverflow.com/questions/12953127/what-are-copy-elision-and-return-value-optimization) –

Respuesta

33

El constructor de copias existe para realizar copias. En teoría, cuando se escribe una línea como:

CLASS c(foo()); 

El compilador tendría que llamar al constructor de copia para copiar el regreso de foo() en c.

La elisión de copia es una técnica para omitir la llamada al constructor de copia para no pagar la sobrecarga. Por ejemplo, el compilador puede arreglar que foo() construya directamente su valor de retorno en c.

Aquí hay otro ejemplo. Digamos que tiene una función:

void doit(CLASS c); 

Si usted lo llama con un argumento real, el compilador tiene que invocar el constructor de copia para que el parámetro original no puede ser modificado:

CLASS c1; 
doit(c1); 

Consideremos ahora un ejemplo diferente, digamos que usted llame a su función como esta:

doit(c1 + c1); 

operator+ va a tener que crear un objeto temporal (un valor de lado derecho). En lugar de invocar el constructor de copia antes de llamar al doit(), el compilador puede pasar el temporal creado por operator+ y pasarlo a doit().

2

Aquí es un ejemplo:

#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(); 
    } 
} 

En somefunction la línea loopcount = loopcount.next(); beneficia enormemente de copia elision. Si no se permitiera la elisión de copia, esa línea requeriría 3 invocaciones del constructor de copia y una llamada asociada a un destructor. Con la elisión de copia permitida, se puede reducir a 1 llamada del constructor de copia, la explícita dentro de BigCount::next() donde se declara newcounter.

Si operator = se había declarado y definido de esta manera:

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

no habría tenido que haber sido 2 invocaciones del constructor de copia, incluso con copia elisión. Uno para construir newcounter y el otro para construir tmp. Y sin elisión de copia todavía habría 3. Por eso, declarar operator = por lo que su argumento requiere invocar el constructo de copia puede ser una optimización cuando se utiliza el modismo 'copiar y intercambiar' para el operador de asignación. Cuando se invoca el constructor de copia para construir un argumento, su invocación puede ser eliminada, pero si se invoca para crear una variable local, puede no serlo.

Cuestiones relacionadas