L'élision de copie est une technique d'optimisation du compilateur qui élimine la copie / déplacement inutile d'objets.
Dans les circonstances suivantes, un compilateur est autorisé à omettre les opérations de copie / déplacement et donc à ne pas appeler le constructeur associé:
- NRVO (Named Return Value Optimization) : si une fonction renvoie un type de classe par valeur et que l'expression de l'instruction return est le nom d'un objet non volatile avec une durée de stockage automatique (qui n'est pas un paramètre de fonction), alors la copie / déplacement qui serait effectuée par un compilateur non optimisant peut être omis. Si tel est le cas, la valeur renvoyée est construite directement dans le stockage vers lequel la valeur de retour de la fonction serait autrement déplacée ou copiée.
- RVO (Return Value Optimization) : si la fonction renvoie un objet temporaire sans nom qui serait déplacé ou copié dans la destination par un compilateur naïf, la copie ou le déplacement peut être omis selon 1.
#include <iostream>
using namespace std;
class ABC
{
public:
const char *a;
ABC()
{ cout<<"Constructor"<<endl; }
ABC(const char *ptr)
{ cout<<"Constructor"<<endl; }
ABC(ABC &obj)
{ cout<<"copy constructor"<<endl;}
ABC(ABC&& obj)
{ cout<<"Move constructor"<<endl; }
~ABC()
{ cout<<"Destructor"<<endl; }
};
ABC fun123()
{ ABC obj; return obj; }
ABC xyz123()
{ return ABC(); }
int main()
{
ABC abc;
ABC obj1(fun123());//NRVO
ABC obj2(xyz123());//NRVO
ABC xyz = "Stack Overflow";//RVO
return 0;
}
**Output without -fno-elide-constructors**
root@ajay-PC:/home/ajay/c++# ./a.out
Constructor
Constructor
Constructor
Constructor
Destructor
Destructor
Destructor
Destructor
**Output with -fno-elide-constructors**
root@ajay-PC:/home/ajay/c++# g++ -std=c++11 copy_elision.cpp -fno-elide-constructors
root@ajay-PC:/home/ajay/c++# ./a.out
Constructor
Constructor
Move constructor
Destructor
Move constructor
Destructor
Constructor
Move constructor
Destructor
Move constructor
Destructor
Constructor
Move constructor
Destructor
Destructor
Destructor
Destructor
Destructor
Même lorsque la suppression de copie a lieu et que le constructeur de copie / déplacement n'est pas appelé, il doit être présent et accessible (comme si aucune optimisation ne s'est produite), sinon le programme est mal formé.
Vous ne devez autoriser cette élision de copie que dans des endroits où elle n'affectera pas le comportement observable de votre logiciel. L'élision de copie est la seule forme d'optimisation autorisée à avoir (c'est-à-dire élide) des effets secondaires observables. Exemple:
#include <iostream>
int n = 0;
class ABC
{ public:
ABC(int) {}
ABC(const ABC& a) { ++n; } // the copy constructor has a visible side effect
}; // it modifies an object with static storage duration
int main()
{
ABC c1(21); // direct-initialization, calls C::C(42)
ABC c2 = ABC(21); // copy-initialization, calls C::C( C(42) )
std::cout << n << std::endl; // prints 0 if the copy was elided, 1 otherwise
return 0;
}
Output without -fno-elide-constructors
root@ajay-PC:/home/ayadav# g++ -std=c++11 copy_elision.cpp
root@ajay-PC:/home/ayadav# ./a.out
0
Output with -fno-elide-constructors
root@ajay-PC:/home/ayadav# g++ -std=c++11 copy_elision.cpp -fno-elide-constructors
root@ajay-PC:/home/ayadav# ./a.out
1
GCC offre la -fno-elide-constructors
possibilité de désactiver l'élision de copie. Si vous souhaitez éviter une élision de copie possible, utilisez -fno-elide-constructors
.
Désormais, presque tous les compilateurs fournissent une élision de copie lorsque l'optimisation est activée (et si aucune autre option n'est définie pour la désactiver).
Conclusion
Avec chaque élision de copie, une construction et une destruction correspondante de la copie sont omises, économisant ainsi du temps CPU, et un objet n'est pas créé, économisant ainsi de l'espace sur le cadre de pile.