IdentifiantMot de passe
Loading...
Mot de passe oubli� ?Je m'inscris ! (gratuit)
logo

FAQ C++Consultez toutes les FAQ

Nombre d'auteurs : 34, nombre de questions : 368, derni�re mise � jour : 14 novembre 2021  Ajouter une question

 

Cette FAQ a �t� r�alis�e � partir des questions fr�quemment pos�es sur les forums de http://www.developpez.com et de l'exp�rience personnelle des auteurs.

Je tiens � souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle propose sont correctes ; les auteurs font le maximum, mais l'erreur est humaine. Cette FAQ ne pr�tend pas non plus �tre compl�te. Si vous trouvez une erreur ou si vous souhaitez devenir r�dacteur, lisez ceci.

Sur ce, nous vous souhaitons une bonne lecture.

SommaireLes fonctionsOptimisation (5)
pr�c�dent sommaire suivant
 

La RVO (pour Return Value Optimization, optimisation de la valeur de retour) est une optimisation automatique du code source effectu�e par la plupart des compilateurs C++.
Cette optimisation s'applique lors d'un appel de fonction renvoyant un objet dont la copie est couteuse. Elle consiste � modifier la fa�on dont la fonction renvoie son objet de retour, de telle fa�on � ce qu'aucune copie dudit objet ne soit cr��e. Il en r�sulte alors un code potentiellement bien plus rapide.
Prenons comme exemple le code suivant.Celui-ci d�finit tout d'abord une classe car (voiture), compos�e de nombreux objets. Cette classe comprend un constructeur, un constructeur par copie et un destructeur.

Code c++ : S�lectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class car 
{ 
public: 
    //constructeur 
    car 
    ( 
        const engine& e, //moteur 
        const wheels& w, //roues 
        const doors& d, //porti�res 
        const steering_wheel& s //volant 
        // etc. 
    ); 
  
    // constructeur par copie 
    car(const car& c); 
  
    // destructeur 
    ~car(); 
  
private: 
    engine engine_; 
    wheels wheels_; 
    doors doors_; 
    steering_wheel steering_wheel_; 
    // d'autres composants... 
};
Les constructeurs et destructeur contiennent une instruction d'�criture sur la sortie standard. Ainsi, nous aurons un rapport d�taill� des diff�rentes op�rations effectu�es.

Code c++ : S�lectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
car::car 
( 
    const engine& e, 
    const wheels& w, 
    const doors& d, 
    const steering_wheel& s 
    // etc. 
): 
  engine_(e), 
  wheels_(w), 
  doors_(d), 
  steering_wheel_(s) 
  // etc. 
{ 
    std::cout << "Appel au constructeur de car.\n"; 
} 
  
car::car(const car& s): 
  engine_(s.engine_), 
  wheels_(s.wheels_), 
  doors_(s.doors_), 
  steering_wheel_(s.steering_wheel_) 
  // etc. 
{ 
    std::cout << "Appel au constructeur par copie de car.\n"; 
} 
  
car::~car() 
{ 
    std::cout << "Appel au destructeur de car.\n"; 
}
Notre code contient �galement une fonction build_sport_car(), renvoyant un objet de type car :

Code c++ : S�lectionner tout
1
2
3
4
5
6
7
8
9
10
const car build_sport_car() 
{ 
    const engine e = build_sport_engine(); 
    const wheels w = build_sport_wheels(); 
    const doors d = build_sport_doors(); 
    const steering_wheel s = build_sport_steering_wheel(); 
    //fabrication des autres composants... 
  
    return car(e, w, d, s); 
}
Enfin, la fonction main() se contente d'appeler la fonction build_sport_car() :

Code c++ : S�lectionner tout
1
2
3
4
5
6
7
int main() 
{ 
    car my_car = build_sport_car(); 
  
    std::cout << "Fin du programme.\n"; 
    return 0; 
}
Si l'on d�sactive la RVO, le programme produit la sortie suivante :

Code : S�lectionner tout
1
2
3
4
5
6
7
Appel au constructeur de car. (1) 
Appel au constructeur par copie de car. (2) 
Appel au destructeur de car. (3) 
Appel au constructeur par copie de car. (4) 
Appel au destructeur de car. (5) 
Fin du programme. 
Appel au destructeur de car. (6)
  1. Tout d'abord, la fonction build_sport_car(), appel�e depuis main(), cr�e une instance de car.
  2. Le retour de l'objet cr�� se traduit par sa copie vers un objet temporaire anonyme. Cet objet temporaire sera en fait la valeur de l'expression � build_sport_car() � situ�e dans la fonction main().
  3. Une fois l'objet copi� avec succ�s, la fonction build_sport_car() se termine. Les objets de la pile cr��s par cette fonction, comprenant l'instance de car, sont d�truits un � un.
  4. De retour � la fonction main(), c'est au tour de l'objet temporaire anonyme d'�tre copi� dans l'objet my_car
  5. � avant d'�tre d�truit � son tour.
  6. Enfin, le programme se terminant, l'objet my_car est d�truit. Il n'existe alors plus d'instance de car.

Activons maintenant la RVO (il s'agit du r�glage par d�faut de tous les compilateurs supportant cette optimisation).
Le programme produit une sortie tout � fait diff�rente.

Code : S�lectionner tout
1
2
3
Appel au constructeur de car. 
Fin du programme. 
Appel au destructeur de car.
Comme dans le cas pr�c�dent, la fonction build_sport_car() cr�e l'instance de car. Mais cette fois-ci, aucun objet temporaire n'est cr�� et aucune copie vers l'objet my_car n'est effectu�e.
Le compilateur a d�tect� que l'instance anonyme de voiture cr��e dans la fonction build_sport_car() devait �tre copi�e vers l'objet my_car.
Une fois l'optimisation appliqu�e, ces deux objets ne font plus qu'un. Nous �vitons alors deux copies et deux destructions, et gagnons ainsi en vitesse d'ex�cution.

Mis � jour le 15 octobre 2009 Florian Goo

Un type d'optimisation similaire peut �galement �tre appliqu� dans le cas o� l'instance de retour de la fonction est nomm�e. On parle alors de NRVO (Named Return Value Optimization, pour optimisation de la valeur de retour nomm�e).

Code c++ : S�lectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const car build_sport_car() 
{ 
    const engine e = build_sport_engine(); 
    const wheels w = build_sport_wheels(); 
    const doors d = build_sport_doors(); 
    const steering_wheel s = build_sport_steering_wheel(); 
    // fabrication des autres composants... 
  
    car sport_car(e, w, d, s); //l'instance est nomm�e 
    sport_car.price(75000); 
    sport_car.build_date(today()); 
  
    return sport_car; 
}

Mis � jour le 15 octobre 2009 Florian Goo

L� o� il est int�ressant d'�tre conscient de l'existence de ces deux types d'optimisation, c'est que leur application d�pend de la fa�on dont sont �crites les fonctions concern�es.
En effet, il pourra �tre impossible pour le compilateur de d�terminer l'instance de retour si la fonction comporte plusieurs points de sortie (plusieurs instructions return) avec des instances diff�rentes.
Par exemple, la NRVO ne pourra �tre appliqu�e au code suivant :

Code c++ : S�lectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const car build_car(const bool type_sport = true) 
{ 
    if(type_sport) 
    { 
        const engine e = build_sport_engine(); 
        const wheels w = build_sport_wheels(); 
        const doors d = build_sport_doors(); 
        const steering_wheel s = build_sport_steering_wheel(); 
        // fabrication des autres composants... 
  
        car sport_car(e, w, d, s); //une premi�re instance. 
  
        sport_car.price(75000); 
  
        return sport_car; //un premier point de sortie. 
    } 
    else 
    { 
        const engine e = build_town_engine(); 
        const wheels w = build_town_wheels(); 
        const doors d = build_town_doors(); 
        const steering_wheel s = build_town_steering_wheel(); 
        // fabrication des autres composants... 
  
        car town_car(e, w, d, s); // une autre instance. 
  
        town_car.price(28000); 
  
        return town_car; // un autre point de sortie. 
    } 
}
En revanche, un l�ger r�am�nagement du code rendra son application possible :

Code c++ : S�lectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const car 
build_car(const bool sport_type = true) 
{ 
    std::unique_ptr<engine> e; 
    std::unique_ptr<wheels> w; 
    std::unique_ptr<doors> d; 
    std::unique_ptr<steering_wheel> s; 
  
    if(sport_type) 
    { 
        e = std::unique_ptr<engine>(new engine(build_sport_engine())); 
        w = std::unique_ptr<wheels>(new wheels(build_sport_wheels())); 
        d = std::unique_ptr<doors>(new doors(build_sport_doors())); 
        s = std::unique_ptr<steering_wheel>(new steering_wheel(build_sport_steering_wheel())); 
        // fabrication des autres composants... 
    } 
    else 
    { 
        e = std::unique_ptr<engine>(new engine(build_town_engine())); 
        w = std::unique_ptr<wheels>(new wheels(build_town_wheels())); 
        d = std::unique_ptr<doors>(new doors(build_town_doors())); 
        s = std::unique_ptr<steering_wheel>(new steering_wheel(build_town_steering_wheel())); 
        // fabrication des autres composants... 
    } 
  
    car new_car(*e, *w, *d, *s); // une seule instance 
    new_car.price(sport_type ? 75000 : 28000); 
  
    return new_car; // un seul point de sortie 
}

Mis � jour le 15 mai 2018 Florian Goo

Tout bon cours sur le C++ vous dira qu'il est pr�f�rable de passer les objets aux fonctions par r�f�rence constante plut�t que par valeur. Il existe cependant un cas o� cette assertion est fausse : celui o� la (N)RVO s'en m�le.
Soit une fonction effectuant une copie, puis une suite de modifications d'une instance de voiture. Nommons simplement cette fonction copy_and_modify_car().
La fonction copy_and_modify_car() est appel�e depuis la fonction main() de la fa�on suivante :

Code c++ : S�lectionner tout
1
2
3
4
5
6
int main() 
{ 
    copy_and_modify_car(build_car()); //on passe directement le retour de la fonction build_car() 
    std::cout << "Fin du programme.\n"; 
    return 0; 
}
Commen�ons par �crire cette fonction de fa�on acad�mique, en prenant une r�f�rence constante :

Code c++ : S�lectionner tout
1
2
3
4
5
void copy_and_modify_car(const car& const_c) 
{ 
    car c(const_c); 
    // modifier c... 
}
La (N)RVO agissant, le programme produit la sortie suivante :

Code : S�lectionner tout
1
2
3
4
5
Appel au constructeur de car. 
Appel au constructeur par copie de car. 
Appel au destructeur de car. 
Fin du programme. 
Appel au destructeur de car.
�crivons maintenant une nouvelle version de cette fonction, en passant l'instance par valeur :

Code c++ : S�lectionner tout
1
2
3
4
void copy_and_modify_car(car c) 
{ 
    // modifier c... 
}
Contre toute attente, aucune copie implicite n'est cr��e :

Code : S�lectionner tout
1
2
3
Appel au constructeur de car. 
Fin du programme. 
Appel au destructeur de car.
Le principe de la (N)RVO (la suppression des objets temporaires interm�diaires) est ici �tendu au passage d'objet.
L'expression � build_car() � de la fonction main() est associ�e � une instance temporaire de voiture. Cet objet temporaire n'ayant de toute fa�on d'autre destin que d'�tre d�truit une fois la copie implicite effectu�e, le compilateur juge bon (� raison) de le passer directement � la fonction copy_and_modify_car() plut�t que d'en effectuer une copie.
Cons�quence de ce raisonnement : l'utilisation d'une instance nomm�e (donc non-temporaire) emp�che l'application de cette optimisation :

Code c++ : S�lectionner tout
1
2
3
4
5
6
7
8
9
int main() 
{ 
    car c = build_car(); 
    copy_and_modify_car(c); //pas d'optimisation li�e � la (N)RVO. 
  
    // �tant donn� que c peut toujours �tre utilis� dans la suite du programme 
  
    return 0; 
}

Mis � jour le 15 octobre 2009 Florian Goo

Il est important de noter que cette optimisation est implicite. Par cons�quent, elle ne permet en aucun cas de retourner des objets d'un type d�fini comme non-copiable.
En effet, l'�criture d'une fonction retournant un tel objet m�nerait � une erreur de compilation, m�me si en pratique aucune copie n'aurait �t� effectu�e.
Heureusement, le standard du langage C++11 introduit la notion de s�mantique de mouvement, permettant de passer outre cette limitation.

Mis � jour le 15 octobre 2009 Florian Goo

Proposer une nouvelle r�ponse sur la FAQ

Ce n'est pas l'endroit pour poser des questions, allez plut�t sur le forum de la rubrique pour �a


R�ponse � la question

Liens sous la question
pr�c�dent sommaire suivant
 

Les sources pr�sent�es sur cette page sont libres de droits et vous pouvez les utiliser � votre convenance. Par contre, la page de pr�sentation constitue une �uvre intellectuelle prot�g�e par les droits d'auteur. Copyright � 2025 Developpez Developpez LLC. Tous droits r�serv�s Developpez LLC. Aucune reproduction, m�me partielle, ne peut �tre faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'� trois ans de prison et jusqu'� 300 000 � de dommages et int�r�ts.