2010-01-27 1 views
29

Je lisais Copy and Swap.Qu'est-ce que l'élision de la copie et comment optimise-t-elle l'idiome copy-and-swap?

J'ai essayé de lire certains liens sur Copy Elision, mais je n'ai pas compris ce que cela signifiait. Quelqu'un peut-il expliquer ce que cette optimisation est, et surtout ce que signifie le texte suivant

Ceci n'est pas seulement une question de commodité, mais en fait une optimisation. Si le (s) paramètre (s) se lie à un lvalue (un autre objet non-const), une copie de l'objet est faite automatiquement lors de la création du (des) paramètre (s). Cependant, lorsque s se lie à un rvalue (objet temporaire, littéral), la copie est généralement éludée, ce qui enregistre un appel à un constructeur de copie et à un destructeur. Dans la version antérieure de l'opérateur d'affectation où le paramètre est accepté comme référence constante, l'élision de la copie ne se produit pas lorsque la référence se lie à une valeur de référence. Cela entraîne la création et la destruction d'un objet supplémentaire.

+0

En relation: [Qu'est-ce que copy elision?] (Http://stackoverflow.com/questions/12953127/what-are-copy-elision-and-return-value-optimization) –

Répondre

33

Le constructeur de copie existe pour faire des copies. En théorie, lorsque vous écrivez une ligne comme:

CLASS c(foo()); 

Le compilateur devrait appeler le constructeur de copie pour copier le retour de foo() dans c. Copier l'élision est une technique permettant d'ignorer l'appel du constructeur de copie afin de ne pas payer pour le surdébit.

Par exemple, le compilateur peut faire en sorte que foo() construise directement sa valeur de retour en c.

Voici un autre exemple. Disons que vous avez une fonction:

void doit(CLASS c); 

Si vous l'appelez avec un argument réel, le compilateur doit appeler le constructeur de copie afin que le paramètre d'origine ne peut pas être modifié:

CLASS c1; 
doit(c1); 

Mais maintenant considérer un autre exemple, disons que vous appelez votre fonction comme ceci:

doit(c1 + c1); 

operator+ va devoir créer un objet temporaire (un rvalue). Au lieu d'appeler le constructeur de copie avant d'appeler doit(), le compilateur peut passer le temporaire qui a été créé par operator+ et le transmettre à doit() à la place.

2

Voici un exemple:

#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 ligne loopcount = loopcount.next(); profite grandement de la copie élision. Si l'élision de la copie n'était pas autorisée, cette ligne nécessiterait trois invocations du constructeur de copie et un appel associé à un destructeur. Lorsque l'élision de la copie est autorisée, elle peut être réduite à 1 appel du constructeur de la copie, le caractère explicite à l'intérieur de BigCount::next()newcounter est déclaré.

Si operator = avait été déclarée et définie comme ceci:

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

il aurait dû avoir été 2 invocations du constructeur de copie, même avec copie élision. Un pour construire newcounter et l'autre pour construire tmp. Et sans élision de la copie il y aurait encore 3. C'est pourquoi la déclaration operator = de sorte que son argument nécessite l'invocation de la construction de copie peut être une optimisation lors de l'utilisation de l'idiome 'copy and swap' pour l'opérateur d'affectation. Lorsque le constructeur de copie est invoqué pour construire un argument, son invocation peut être élidée, mais s'il est appelé pour créer une variable locale, il peut ne pas l'être.

Questions connexes