encore bonjour,
est ce que vous savez comment gcc compile l'opérateur ( bool ? true : false ) ??
est ce que c'est simplement l'équivalent d'un if avec affection d'une variable temporaire, ou est ce qu'il y a des optimisations de faites ?
Version imprimable
encore bonjour,
est ce que vous savez comment gcc compile l'opérateur ( bool ? true : false ) ??
est ce que c'est simplement l'équivalent d'un if avec affection d'une variable temporaire, ou est ce qu'il y a des optimisations de faites ?
Oui, comme tous les compilateurs C++, et C aussi d'ailleurs.
Pour ce qui est de l'optimisation, elle n'est pas différente de n'importe quelle formule mathématique.
est ce que tu peut clarifier ta réponse stp...Citation:
Envoyé par zais_ethael
"Oui" à quelle question
et de quelle formule mathématique parles tu? on parle bien d'un "if" la, n'est ce pas ?
Il se démerde, c'est son problème.Citation:
est ce que vous savez comment gcc compile l'opérateur ( bool ? true : false ) ??
Question performances, c'est kif-kif, le choix se fait plutôt sur la solution qui est la plus lisible, la plus facile à écrire, la plus maintenable., et ça, ça dépend du contexte.Citation:
est ce que c'est simplement l'équivalent d'un if avec affection d'une variable temporaire, ou est ce qu'il y a des optimisations de faites ?
Merci c'est ce que j'avais besoin de savoir, mais sur quoi tu te base pour affirmer cela ?Citation:
Envoyé par Sylvain Togni
Quand on sait pas, on se tait ^^Citation:
Envoyé par Sylvain Togni
En plusieurs années de programmation je n'ai jamais eu à me poser cette question. Et quand bien même je voudrais le savoir, j'examinerais le code source ASM produit par le compilateur. De toute façon le résultat dépendra du contexte : dans certains cas le compilo pourra optimiser, dans d'autres non.Citation:
Quand on sait pas, on se tait ^^
Il est temps de clore le sujet alors si personne ne veut connaitre la réponse (sauf moi on dirait..)Citation:
Envoyé par Laurent Gomila
Salut,
En fait, il faut bien retenir qu'un code fonctionnel est beaucoup plus souvent lu qu'il n'est écrit.
Le premier rôle d'un code source est donc d'être facilement compris par la personne qui l'a entre les mains :D
L'opérateur ternaire ? est, certes, intéressant pour l'écriture du code, mais selon sa position dans le reste du code, selon ce qu'il fait dans les deux cas, selon son rôle, il a le très gros défaut de... rendre le code plus compliqué à lire.
Dans certains cas, ce n'est pas "catastrophique":
ne posera que peu de problème à la lecture, mais, dans d'autres cas il y a clairement moyen de faire quelque chose d'incompréhensible :PCode:
1
2
3
4
5
6
7 void fonct(int i) { cout<<"salut " <<(i>3 ? "la populace" :"tout le monde") <<endl; }
devient déjà bien moins évident à comprendre, bien que ce soit un code parfaitement valide.Code:
1
2
3
4
5
6 void fonct(int i) { cout<<"salut " <<(i==0 ? "toi":(i>3 ? "la populace":"tout le monde")); }
Dans le premier cas, quand dans trois mois tu reverra le code, tu le comprendra *relativement* facilement, alors que dans le deuxième cas, il te faudra quand même quelques secondes de réflexion pour savoir ce que tu a voulu écrire :D
C'est tout simplement là dessus que l'on se base pour dire que la facilité d'écriture, d'entretien et de lecture dépend fortement du contexte dans lequel tu utilise l'opérateur ternaire ;)
Pour ce qui est de la manière dont le compilateur va le gérer n'intéresse... que le compilateur et ceux qui créent le compilateur... Ce qu'on demande, c'est qu'il puisse le gérer correctement ;)
Merci pour cette réponse claire (la seule de ce post), qui vient confirmer mon opinion de ne pas utiliser cet opérateur trop souvent.
Cependant ma réponse reste, car ce qui m'est venu à l'esprit en premier lieu, c'est : est ce qu'il aurait une différence de performance entre ces deux manières d'écrire ?
Je ne comprends le refus général qu'il y a au fait de savoir s'il on peut utiliser cet instructions pour optimiser certains endroits du code.
Non, en général la seule optimisation permise par cet opérateur est une optimisation de lignes de code. Une fois compilé (et hors optimisations), il y aura quoiqu'il arrive un test et un saut.
C'est une opération assez basique, il n'y a pas 50 manières de la coder en langage machine.
ok merci. Je m'en doutais un peu, je voulais juste une petite confirmation, rien de plus
Pour préciser un peu ce qu'a écrit Laurent...
Il faut bien te dire que le rôle du compilateur est... de transformer quelque chose qui est lisible par l'humain ... en quelque chose qui est utilisable par le processeur...
Pour que le processeur puisse effectuer un test, il ne dispose que d'une liste bien précise d'instructions (cmp, jmp, jne, je et quelques autres)
A partir du moment ou un branchement doit être fait de manière conditionnelle, on "retombera" systématiquement sur l'une ou l'autre des instructions de cette liste :D
Pourtant, c'est clairement indiqué plus haut, ça ne change rien à par la lisibilité.Citation:
Envoyé par mamelouk
Ce qui veut dire qu'il y a beaucoup de réponses plus que pertinentes sur ce topic, et en revanche une question qui montre clairement que tu n'as pas correctement lu ton cours/livre sur le C++.
Si je fais remonter ce thread, c'est pour une dernière précision:
dans 90 à 95 % des cas, l'opérateur ternaire pourra être joyeusement remplacé par un if else (ou un switch), ne serait-ce que pour une question de facilité de relecture (étant entendu que, si gain il y a, il ne sera clairement pas significatif ;))
Par contre, il reste les 5 à 10 derniers % des cas dans lesquels leur utilisation est, pour le moins, justifiée - à défaut de pouvoir assurer qu'elle est incontournable (si l'un des "pontes" pouvait confirmer ou infirmer :question:).
Ainsi, je m'intéresse pour l'instant à la bibliothèque loki, qui travaille énormément avec les template.
Dans cette bibliothèque, on rencontre entre autre une classe template qui se présente, entre autre, sous la forme de
Je ne suis franchement pas persuadé qu'il y ai moyen d'obtenir le même résultat avec une structure if else correspondant àCode:
1
2
3
4
5
6
7 struct IndexOf<TypeList<head, tail>, T> { private: enum {temp=IndexOf<taile, T>::value}; public: enum (value= temp == -1 ? -1 : 1+temp); };
Encore une fois, il s'agit donc de bien se rendre compte du fait que, certaines techniques peuvent s'avérer être "peu recommandable" pour une majorité des cas, mais tout à fait justifiées (voire, être la seule solution) pour des cas particuliers.Code:
1
2
3
4
5
6
7 enum { if(temp==-1) value=-1; else value=1 + temp; };
Et je peux t'assurer que l'opérateur ternaire est loin d'être le seul exemple de ce fait ;)
Comme les valeurs sont connues à la compilation lorsque l'on manipule les typelists de loki, on arrive à déterminer la valeur à la compilation. Impossible avec un branchement if/else.
En effet, l'opérateur ternaire est pas mal utilisé dans la métaprogrammation.
Il fait partie du bagage dont on dispose pour "métaprogrammer".
( ps : ah ça y est tu t'y mets ;) )
salut,
il y a des optis realisees par le compilauteur parfois
le code siuivant est compilé sans saut
mais le if qui fait la meme chose aussi.Code:
1
2 int i = (test > 0) ? 2 : 3;
je ne suis pas d'accord avec ce que vous dites, la principale difference entre if et ?: c'est que ?: renvoie une valeur et pas if. Donc quand j'ecris la ligne plus haut
ca me parait plus clair que le if.Code:i = (test > 0) ? 2 : 3;
La raison c'est que si je cherche ou est initialisé i je tombe sur un seul endroit, alors qu'avec le if c'est a deux endroits. Si l'expression elle meme me parait moins lisible, je m'en fiche, car quand je cherche un bug, je n'ai pas besoin de comprendre tous le code que je vois; je fais confiance pour que la fonction size() renvoie une taille sans aller verifier le code; je suis sur que i est un compteur de boucle ou une variable temporaire de peu d'interet; je cherche un bug qui a deja un diagnostique.
Le fait de couper une initialisation de variable en 2 ou d'imbriquer des IF complexifie ma recherche.
Sauf dans le cas d'utilisation "trop" complexes (trop imbriquees, expressions trop longue).
Il est possible d'ecrire du ternaire lisible aussi, quand les gens montrent des exemples de ternaire ils montrent toujours des exemples ou les gens mettent meme pas d'espace, mais rien n'interdit le ternaire d'etre ecrit sur plusieurs lignes aussi
Code:
1
2
3
4 int i = (test > 0) ? 2 : 3
merci screetch pour cette clarification.
on m'avais expliqué que le ++i était plus rapide que le i++, du fait que i++ retourne la valeur avant l'incrémentation et est donc obligé de la stocker.
en comparaison, si l'opérateur ?: renvoie une valeur (pour l'affectation) alors que lors de l'utilisation d'un if, l'affectation est faite sans qu'il ait de "return", est ce que cela ne signifie pas qu'un simple if-else est plus rapide ?
j'imagine que le compilateur n'est pas stupide au point de générer une fontion avec "return", mais encore fois ceci n'est qu'une discussion.. Si vous etes trop intelligents pour ne pas supporter mes questions allez troller ailleurs...
C'est valable pour les itérateurs complexes comme les std::vector<>::iterator. Pour les entiers simples, le compilateur optimise sans problème puisque le retour n'est pas utilisé et qu'il connaît parfaitement le fonctionnement des entiers (contrairement aux types plus complexes)
On peut dire que l'opérateur ?: peut être en partie être traité par la partie statique du compilateur (celle qui gère les template), ce qui n'est pas le cas d'un if. Dans le cas dynamique (le test n'est pas fixé à la compilation), le code est sans num doute écrit avec deux affectations et il est équivalent à un if simple.
Le compilateur ne génèrera pas de fonction pour ça. Si tu ne lui dis pas de créer une fonction, elle ne sera pas créée, jamais.
No comment.
++i est rapide pour les types complexes car tu ne dois pas realiser une copie. Realiser une copie d'un entier qui est perdue dans la nature ne coute rien. Executer une copie d'un iterateur de vector implique l'appel d'une methode, d'un constructeur, puis comme tu ne t'en sers pas, d'un destructeur.
La version ++i modifie l'objet "in-place" (sur place) donc bye bye l'appel au constructeur et au destructeur =)
Ensuite pour le envoi d'une valeur, ce n'est pas plus lent.
Dans le pire des cas, l'operateur ternaire se comporte comme un if, c'est a dire qu'il separe ton code en deux branches et effectue l'affectation. Dans le meilleur des cas, il realise une operation sans saut et place l'unique resultat dans un registre, un peu comme quand on fait a+b => realisation de l'operation et stockage du resultat dans un registre.
Pour que l'optimisation sans saut fonctionne il faut que tu lui files du code simple, par exemple un choix entre deux valeurs statiques.
Ci dessus,
est optimise sans saut avec des operations (pour ceux que ca interesse, le compilo genere le code suivant :Code:(test)?1:3;
il existe des tonnes de petites optims pour cela, quelles que soient les valeurs.Code:
1
2
3
4
5
6
7 __asm { XOR eax,eax //mets 0 dans eax TEST test SETLE eax //mets 1 dans eax si le resultat des test etait faux, sinon 0 ADD eax,eax+1 // ajoutes a eax eax+1, donc si eax vaut 0 ca vaut 1 sinon ca vaut 3 }
Mais si le resultat de ?: est a aller chercher dans une variable ou si il faut appeler une fonctio, pas de miracle, le compilo utilisera un saut jusqu'a la branche de code a effectuer.
Ca revient exactement au meme dans un if, mais la detection des optis possibles dans le if est plsu compliquee, a la rigueur tu aides le compilo avec :?
Donc moi quand il s'agit de se servir du resultat je prefere l'operateur ?: car il est comme une fonction (il renvoie une valeur)
quand il s'agit d'executer du code sans renvoyer de valeur j'utilise if puisque chaque bloc est comme une petite procedure (ne renvoie pas de valeur)
si le ?: devient trop complexe, alors je le vire et passe en if pour la lisibilité
Ca, c'est parce que tu as mal indenté ton code.
Je trouve ça aussi lisible que la solution à base de if.Code:
1
2
3
4
5
6
7
8
9 void fonct(int i) { cout<<"salut " <<(i==0 ? "toi": i>3 ? "la populace": "tout le monde"); }
La où je le trouve moins lisible, c'est quand les types sont non identiques, bien que proches, et qu'on doit caster :
Code:
1
2
3
4
5
6
7
8
9
10
11
12 string f(bool b, string s) { return b ? s : string ("Empty"); } string f(bool b, string s) { if (b) return s; else return "Empty"; }
et si on a un code comme celui la :comment est-il "transformé"?Code:printf( "il y a %d element%s ",d,(d==1)?"":"s");
ouCode:
1
2
3
4 if(d==1) printf( "il y a %d element%s ",d,""); else printf( "il y a %d element%s ",d,"s");
ou on peut pas savoir en quoi c'est "transformé" ou encore autre chose qui m'est pas passé par la tête?Code:
1
2
3
4
5
6 char *tmp; if(d==1) tmp = ""; else tmp = "s"; printf("il y a %d element%s",d,tmp);
le deuxieme
Ceci dit, je ferais plutot le test sur d<1...
Juste histoire que le "s" ne soit pas rajouté si d==0 :P
a mon avis, ça doit même être transformé dans un truc comme (sans les optimisations dues au fait qu'on connaisse les chaines de caractères à la compilation) :
Pour que ça puisse marcher pour des classes plus complexesCode:
1
2
3
4
5
6 char **tmp; if(d==1) tmp = &(""); else tmp = &("s"); printf("il y a %d element%s",d,*tmp);
Exemple :
la classe "ma_classe" implémente l'operateur << et n'a pas de constructeur par copie
fourniraiCode:
1
2
3
4 int d; ma_classe chaine_vide,chaine_s; /* ... */ cout << "element" << (d==1)?chaine_vide:chaine_s;
Code:
1
2
3
4
5
6
7
8
9 int d; ma_classe chaine_vide,chaine_s; /* ... */ ma_classe* tmp; if(d==1) tmp = &chaine_vide else tmp = &chaine_s; cout << "element" << *tmp;
Ben c'est relativement simple:
Le pluriel ne s'applique qu'à ce qui dépasse 1:
0=aucun->singulier
1=... 1 :aie:->singulier
1.1> 1 ->pluriel
Les deux seules exceptions sont "plus de un" et "moins de deux", pour la consonnance: ca "sonne mal" de dire "un sont ..." (alors que plus de un, ca commence à deux) ou "deux est ..." (alors que moins de deux, ca fait ... 1 ou 0)
j'avais jamais remarqué ca.
Plus d'un chat EST mort sous les roues d'une voiture
=> mais y'en a plus qu'un! mais on met le singulier.
Tiens rigolo ca ^^
Hé oui, les bizarreries de la langue française :D