Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

retour de fonction, copie d'agument

1 réponse
Avatar
Benoit Dejean
je me pose des questions sur les retours de fonction et les copies

(désolé pour le volume)

#include <iostream>
using namespace std;

class Foo
{
public:

Foo()
{
cout << "Foo()\t"
<< "this = " << this
<< endl;
}

Foo(int i)
{
cout << "Foo(int)\t"
<< "this = " << this
<< endl;
}


Foo(const Foo &other)
{
cout << "Foo(Foo)\t"
<< "this = " << this
<< " other = " << &other
<< endl;
}

~Foo()
{
cout << "~Foo()\t"
<< "this = " << this
<< endl;
}

Foo& operator=(const Foo &other)
{
cout << "opertor=(Foo)\t"
<< "this = " << this
<< " other = " << &other
<< endl;
return *this;
}
};


Foo f()
{
cout << "\nf()\n";
return Foo();
}

Foo g()
{
cout << "\ng()\n";
return Foo(42);
}

Foo h()
{
cout << "\nh()\n";
Foo tmp;
return tmp;
}


Foo i()
{
cout << "\nh()\n";
Foo tmp;
return Foo(tmp);
}

int main()
{
Foo res;

res=f();
res=g();
res=h();
res=i();
}


donne sur mon systeme (linux, g++ 3.3.1)

Foo() this = 0xbffffc30

f()
Foo() this = 0xbffffc20
opertor=(Foo) this = 0xbffffc30 other = 0xbffffc20 ~Foo() this =
0xbffffc20

g()
Foo(int) this = 0xbffffc20
opertor=(Foo) this = 0xbffffc30 other = 0xbffffc20 ~Foo() this =
0xbffffc20

h()
Foo() this = 0xbffffc20
opertor=(Foo) this = 0xbffffc30 other = 0xbffffc20 ~Foo() this =
0xbffffc20

h()
Foo() this = 0xbffffbe0
Foo(Foo) this = 0xbffffc20 other = 0xbffffbe0 ~Foo() this =
0xbffffbe0
opertor=(Foo) this = 0xbffffc30 other = 0xbffffc20 ~Foo() this =
0xbffffc20
~Foo() this = 0xbffffc30


avec VC7, la trace de h() donne

h()
Foo() this = 0012FEB0
Foo(Foo) this = 0012FED7 other = 0012FEB0 ~Foo() this = 0012FEB0
opertor=(Foo) this = 0012FED8 other = 0012FED7 ~Foo() this = 0012FED7
~Foo() this = 0012FED8


c'est à dire qu'il y a un recopie supplémentaire.


vous pouvez m'éclairez sur le comportement de la norme? par ce que j'ai
écrit plein de chose pour réduire le nombre d'objet temporaire dans mes
expressions, et avec VC7, le résultat est catastrophique dans certains
cas, c'est à dire pire qu'avec le comportement par défaut (c'est à dire
sans toutes mes acrobaties)


--
"Ne perdez pas de vue qu'un programme rapide
et incorrect est d'une utilité presque nulle."
Ce qui est loin d'être incompatible avec la notion d'Art.

1 réponse

Avatar
Jean-Marc Bourguet
Benoit Dejean writes:

je me pose des questions sur les retours de fonction et les copies


Il y a deux choses à considérer: le comportement logique et les
optimisations permises par la norme.

Le comportement logique c'est que le retour de fonction utilise le
constructeur de copie.

Les optimisations permises sont au nombres de deux:

- en toute circonstance (donc pas uniquement au retour d'une
fonction), quand un constructeur de copie devrait être appelé
pour copier un temporaire, le compilateur peut utiliser le
temporaire à la place (ou plus vraissemblablement contruire
directement le temporaire)

- lorsqu'on retourne un objet nommé d'une fonction, on peut
utiliser l'objet plutôt que d'en faire une copie (NRVO) (ou plus
vraissemblablement mettre l'objet nommé directement à l'endroit
ou la valeur de retour de la fonction est attendu, donc même si
la norme le permet, je doute qu'un compilateur utilise la NRVO
quand dans des chemins différents on retourne des objets nommés
différents sauf dans des cas particuliers -- voir ci-dessus)

Foo f()
{
cout << "nf()n";
return Foo();
}


On retourne un temporaire:
construction du temporaire
construction de la valeur retournée par copie du temporaire
(peut être supprimée en construisant le temporaire directement en
tant que valeur retournée)
donc 0 ou 1 copie

Foo g()
{
cout << "ng()n";
return Foo(42);
}


Même chose

Foo h()
{
cout << "nh()n";
Foo tmp;
return tmp;
}


On retourne une valeur nommée:
construction de la valuer
construction de la valeur retournée par copie de la valeur nommée
(peut être supprimée en construisant la valeur nommée directement en
tant que valeur retournée)
donc 0 ou 1 copie

Foo i()
{
cout << "nh()n";
Foo tmp;
return Foo(tmp);
}


On retourne un temporaire construit à partir d'une valeur nommée.
construction de la valeur nommée
construction du temporaire par copie de la valeur nommée
construction de la valeur retournée par copie du temporaire
(peut être supprimée en construisant le temporaire directement en
tant que valeur retournée)
donc 1 ou 2 copies


donne sur mon systeme (linux, g++ 3.3.1)


On voit que g++ implémente les deux optimisations

avec VC7, la trace de h() donne

h()
Foo() this = 0012FEB0
Foo(Foo) this = 0012FED7 other = 0012FEB0 ~Foo() this = 0012FEB0
opertor=(Foo) this = 0012FED8 other = 0012FED7 ~Foo() this = 0012FED7
~Foo() this = 0012FED8


et que VC7 n'a pas le NRVO

A propos de ma remarque sur les objets différents, dans ce cas

Foo nrvo(bool b) {
if (b) {
Foo res1;
return res1;
} else {
Foo res2;
return res2;
}
}

rien n'empèche l'application du nrvo (on retourne des objets
différents mais il n'y a pas de problème de destruction car l'autre
objet ne doit pas être détruit), pourtant ni gcc 3.3 ni como ne le
font pas alors qu'ils le font pour

Foo nrvo(bool b) {
Foo res1;
if (b) {
return res1;
} else {
return res1;
}
}

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org