le move est fait avec des rvalue references si j'ai bien compris.
il faut donc le traiter en plus pour pouvoir faire le "move"
le move est fait avec des rvalue references si j'ai bien compris.
il faut donc le traiter en plus pour pouvoir faire le "move"
a mon avis, un déplacement qui sera fait seulement si tu retourne une locale de ta fonction.
sinon ca veut rien dire..
je vois mal l'appel de la fonction foo modifier la variable A::B.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 class A { monGrosObjet foo() {return B;} monGrosObjet foo2() { monGrosObjet aa; [...] return aa; } monGrosObjet B; }
Par contre avec foo2 oui.
Pour le cow, je trouve que qt permet de faire cela de manière trés simple
http://qt.developpez.com/doc/4.4/qshareddatapointer/
Non, dans tous les cas lors d'un retour par valeur.
En C++03, ceci fait deux copies.sinon ca veut rien dire..
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 class A { monGrosObjet foo() {return B;}
En C++0x, ceci fait une copie et un déplacement.
Dans les deux cas, on peut optimiser cela en une seule copie et c'est tout si la valeur de retour est utilisée pour initialiser une variable du même type.
Ceci fait deux copies en C++03, et deux déplacement en C++0x.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 monGrosObjet foo2() { monGrosObjet aa; [...] return aa; } monGrosObjet B; }
Dans un cas comme dans l'autre, on peut optimiser cela à zéro copie et zéro déplacement si la valeur retournée est utilisée pour initialiser une variable de même type.
Tu peux constater tout ça en testant avec GCC 4.3 avec le test suivant
Utilise -std=c++0x pour activer C++0x
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45 #include <iostream> struct monGrosObjet { monGrosObjet() { } monGrosObjet(const monGrosObjet&) { std::cout << "copie" << std::endl; } #ifdef __GXX_EXPERIMENTAL_CXX0X__ monGrosObjet(monGrosObjet&&) { std::cout << "déplacement" << std::endl; } #endif }; struct A { monGrosObjet foo() { return B; } monGrosObjet B; }; monGrosObjet foo2() { monGrosObjet aa; return aa; } int main() { A a; std::cout << "cas 1" << std::endl; monGrosObjet bar = a.foo(); std::cout << "cas 2" << std::endl; monGrosObjet baz = foo2(); }
Utilise -fno-elide-constructors pour désactiver la NRVO.
merci pour les exemples, c'est très instructif.
Les rvalue references me faisaient un peu peur dans leur complexité de mise en oeuvre, la ca me parait super jouable.
edit: j'ai quand meme l'impression quand je lis certains articles qu'il faut renvoyer monobjet&& ...
La NRVO s'applique en C++03 comme en C++0x.
en bref, les rvalue references, c'est le pied mais ca ne sera pas disponible avant un paquet de temps ... alors le COW ? bonne solution pour maintenant?
je parle en general,
pour simplifier la lecture du code et pour optimiser le programme vu qu'il ne fait pas de recopie.
j'ai tellement lu de critiques sur le COW dans Qt (et je la trouve formidable cette lib) que je me suis reposé mainte fois cette question...
J'ai aussi donc tellement lu de critique sur cette QString, que tout le monde donnait perdante face à une sd:string par valeur et un allocateur optimisé...
... et je reviens avec le benchmark pour voir si les gens sont d'accord avec le benchmark et si ca a fait changer leur point de vue...
et toi?
Il me semble qu'implémenter du COW est plus compliqué d'implémenter de la copie au moment de la copie...
La seule réponse est "mesure!", et mesure toi-même de préférence sur le programme déjà bien finalisé, au moins sur des exemples que tu sais être caractéristiques de ton utilisation.et pour optimiser le programme vu qu'il ne fait pas de recopie.
Implémenter la version COW une fois qu'on a identifié que les copies prenaient du temps, ca devrait être relativement rapide... surtout si tu as une classe qui est destinée à aider à faire cela.
Faire de l'optimisation une fois que les approches algorithmiques évidentes ont été épuisées et gratter 2% par ici, 3% par là? Oui, j'ai déjà fait et c'est long, et ça demande des données vraisemblables -- le mieux c'est des données qui viennent de production -- parce que c'est sensible... tellement sensible qu'on se demande si ça a du sens de faire cela.
J'ai déjà aussi gagné des facteurs 1000 en faisant à moitié rien... une fois qu'on à trouvé que le cas qui semblait ne jamais devoir arriver arrivait bien ou qu'on vient de remarquer que l'opération qui semblait élémentaire ne l'est pas.
Pour revenir à ton sujet, le COW retarde un coût unique supposé important au prix d'un coût distribué supposé faible en espérant que le coût unique n'arrivera pas. Pour savoir si c'est gagnant ou non, il faut connaître la valeur de ces coûts, le nombre de fois où on évite le coût important et le nombre de fois qu'on paye le coût faible. Ça me semble très difficile à prévoir.
C'est déjà disponible dans un certain nombre de compilateurs.en bref, les rvalue references, c'est le pied mais ca ne sera pas disponible avant un paquet de temps ...
Le COW, ça sert à optimiser les cas où on copie mais où en fait on ne veut pas copier mais simplement déplacer.
Si on dispose des sémantiques de déplacement, il suffit de déplacer quand on veut déplacer et copier quand on veut copier. Pas besoin de comptage de référence et de copie paresseuse lors d'une écriture.
C'est donc une solution bien plus élégante, mais aussi plus performante.
Après, les sémantiques de mouvement ont tout de même un certain nombre de problèmes, qui n'en font pas une solution parfaite non plus.
Le principal problème est qu'il faut laisser l'objet déplacé dans un état vide valide pour certains opérations (qui ne sont pas encore bien définies à mon goût), ce qui peut briser des invariants ou nécessiter de faire des cas particuliers pour l'implémentation de certaines primitives.
L'idéal ça aurait été des sémantiques de déplacement destructices, mais c'est trop tard pour ça maintenant.
b- Un compteur atomique n'est pas exactement gratuit, même si négligeable face à un lock.
a- Et hormis les retours de fonctions, si tu as besoin du COW, très bien, sert-en -- j'ai du mal à évaluer, cela n'a jamais été mon cas.
Et concernant les retours de fonctions, les rvalue references devraient enterrer le COW... si on oublie que c'est une nouvelle syntaxe, et que beaucoup persistent toujours à passer leurs chaines par copie. (en plus des problèmes signalés par Loufoque)
Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...
Tant que ce n'est pas disponible dans tous les compilateurs cibles et ceux vraisemblablement utilisé dans un proche avenir, ça ne sert pas à grand chose.
Aussi -- surtout -- les cas où on ne modifie pas et donc où toutes les occurrences peuvent référencer la même zone mémoire.Le COW, ça sert à optimiser les cas où on copie mais où en fait on ne veut pas copier mais simplement déplacer.
Partager