J'apprends actuellement le C ++ avec le livre C ++ Primer et l'un des exercices du livre est:
Expliquez ce que fait l'expression suivante:
someValue ? ++x, ++y : --x, --y
Que savons-nous? Nous savons que l'opérateur ternaire a une priorité plus élevée que l'opérateur virgule. Avec les opérateurs binaires, c'était assez facile à comprendre, mais avec l'opérateur ternaire, je me bats un peu. Avec les opérateurs binaires "ayant une priorité plus élevée" signifie que nous pouvons utiliser des parenthèses autour de l'expression avec une priorité plus élevée et cela ne changera pas l'exécution.
Pour l'opérateur ternaire, je ferais:
(someValue ? ++x, ++y : --x, --y)
résultant effectivement dans le même code qui ne m'aide pas à comprendre comment le compilateur regroupera le code.
Cependant, après avoir testé avec un compilateur C ++, je sais que l'expression se compile et je ne sais pas ce qu'un :
opérateur pourrait représenter par lui-même. Le compilateur semble donc interpréter correctement l'opérateur ternaire.
Ensuite, j'ai exécuté le programme de deux manières:
#include <iostream>
int main()
{
bool someValue = true;
int x = 10, y = 10;
someValue ? ++x, ++y : --x, --y;
std::cout << x << " " << y << std::endl;
return 0;
}
Résulte en:
11 10
Alors que d'autre part avec someValue = false
elle imprime:
9 9
Pourquoi le compilateur C ++ générerait-il du code qui pour la vraie branche de l'opérateur ternaire n'incrémente que x
, tandis que pour la fausse branche du ternaire, il décrémente à la fois x
et y
?
Je suis même allé jusqu'à mettre des parenthèses autour de la vraie branche comme ceci:
someValue ? (++x, ++y) : --x, --y;
mais il en résulte toujours 11 10
.
la source
?
est l' opérateur conditionnel . Le terme opérateur ternaire signifie simplement un opérateur avec trois opérandes. L'opérateur conditionnel est un exemple d' opérateur ternaire, mais un langage pourrait (théoriquement) avoir plusieurs opérateurs ternaires.Réponses:
Comme l'a dit @Rakete dans leur excellente réponse, c'est délicat. J'aimerais ajouter quelque chose à cela.
L'opérateur ternaire doit avoir la forme:
Nous avons donc les mappages suivants:
someValue
: expression-logique++x, ++y
: expression--x, --y
ou seulement--x
?En fait, c'est uniquement
--x
parce qu'une expression d'affectation ne peut pas être analysée comme deux expressions séparées par une virgule (selon les règles de grammaire de C ++) et--x, --y
ne peut donc pas être traitée comme une expression d'affectation .Ce qui donne à la partie d'expression ternaire (conditionnelle) l'aspect suivant:
Pour des raisons de lisibilité, il peut être utile de considérer
++x,++y
qu'il est calculé comme s'il était entre parenthèses(++x,++y)
; tout ce qui est contenu entre?
et:
sera séquencé après le conditionnel. (Je vais les mettre entre parenthèses pour le reste de l'article).et évalué dans cet ordre:
someValue?
(++x,++y)
ou--x
(selon lebool
résultat de 1.)Cette expression est ensuite traitée comme la sous-expression gauche d'un opérateur virgule, la sous-expression droite étant
--y
, comme ceci:Ce qui signifie que le côté gauche est une expression à valeur rejetée , ce qui signifie qu'il est définitivement évalué, mais nous évaluons ensuite le côté droit et le retournons.
Alors qu'est - ce qui se passe quand
someValue
esttrue
?(someValue?(++x,++y):--x)
exécute et incrémentex
ety
être11
et11
--y
qui décrémentey
ensuite10
Pour "corriger" le comportement, vous pouvez le regrouper
--x, --y
avec des parenthèses pour le transformer en une expression principale qui est une entrée valide pour une expression d'affectation *:* C'est une longue chaîne plutôt amusante qui relie une expression d'affectation à une expression principale:
cession d'expression --- (peut consister) -> conditionnel expression -> ou expression logique -> et l' expression logique -> inclusive ou expression -> ou expression exclusive - -> et-expression -> égalité-expression -> relation-expression -> shift-expression -> additive-expression -> multiplicative-expression -> pm-expression -> cast-expression -> expression-unaire -> expression-suffixe -> expression-primaire
la source
{ ... }
peuvent être traités comme une expression), j'ai maintenant une réponse => c'est pour éviter d'avoir à introduire un opérateur virgule qui se comporte de manière si délicate.assignment-expression
chaîne?Wow, c'est délicat.
Le compilateur voit votre expression comme:
L'opérateur ternaire a besoin de a
:
, il ne peut pas se tenir seul dans ce contexte, mais après cela, il n'y a aucune raison pour que la virgule appartienne au cas faux.La raison pour laquelle vous obtenez cette sortie peut maintenant être plus logique. Si
someValue
est vrai, alors++x
,++y
et--y
est exécuté, ce qui ne change pas efficacementy
mais en ajoute un àx
.Si
someValue
est faux, alors--x
et--y
sont exécutés, en les décrémentant tous les deux de un.la source
Vous avez mal interprété ce qui s'est passé. La vraie branche incrémente à la fois
x
ety
. Cependant,y
est décrémenté immédiatement après cela, sans condition.Voici comment cela se produit: puisque l'opérateur conditionnel a une priorité plus élevée que l'opérateur virgule en C ++ , le compilateur analyse l'expression comme suit:
Notez le "orphelin"
--y
après la virgule. C'est ce qui conduit à une décrémentationy
qui a été initialement incrémentée.Vous étiez sur le bon chemin, mais vous avez mis entre parenthèses une mauvaise branche: vous pouvez résoudre ce problème en mettant entre parenthèses la branche else, comme ceci:
Démo (imprime 11 11)
la source
Votre problème est que l'expression ternaire n'a pas vraiment de priorité plus élevée que la virgule. En fait, C ++ ne peut pas être décrit avec précision simplement par priorité - et c'est exactement l'interaction entre l'opérateur ternaire et la virgule où il se décompose.
est traité comme:
(la virgule se comporte comme si elle avait une priorité plus élevée). D'autre part,
est traité comme:
et l'opérateur ternaire a une priorité plus élevée.
la source
Un point qui a été négligé dans les réponses (bien qu'effleuré dans les commentaires) est que l'opérateur conditionnel est invariablement utilisé (prévu par la conception?) Dans le code réel comme raccourci pour attribuer l'une des deux valeurs à une variable.
Ainsi, le contexte plus large serait:
Ce qui est absurde à première vue, les crimes sont donc multiples:
la source
if
(par exemple, l'expression d'incrémentation dans une boucle for). Le contexte plus large pourrait bien êtrefor (x = 0, y=0; x+y < 100; someValue?(++x, ++y) :( --x, --y))
avec une boucle qui peut modifierx
ety
indépendamment.