118 votes

Pourquoi l'opérateur ternaire avec des virgules n'évalue-t-il qu'une seule expression dans le cas réel?

Je suis présentement en train d'apprendre le C++ avec le livre C++ Primer et l'un des exercices du livre est:

Expliquer ce qu'est l'expression suivante n': 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 des opérateurs binaires c'était assez facile à comprendre, mais avec l'opérateur ternaire, je suis un peu en difficulté. Avec des opérateurs binaires "ayant une priorité plus élevée" signifie que nous pouvons utiliser des parenthèses autour de l'expression de priorité plus élevée et il ne changera pas l'exécution.

Pour l'opérateur ternaire que je ferais:

(someValue ? ++x, ++y : --x, --y)

effectivement le même code qui ne m'aide pas à comprendre comment le compilateur groupe le code.

Cependant, à partir de tests avec un compilateur C++ je sais que l'expression compile et je ne sais pas ce qu'est un : opérateur pourrait se tenir par lui-même. Ainsi, le compilateur semble interpréter l'opérateur ternaire correctement.

Puis j'ai exécuté le programme de deux façons:

#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ésultats:

11 10

Tandis que de l'autre main avec someValue = false imprimés:

9 9

Pourquoi le compilateur C++ de générer le code que pour le vrai-direction générale de l'opérateur ternaire seulement incréments x, tandis que pour les faux-direction générale de l'ternaire, il décrémente les deux 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 reste encore des résultats en 11 10.

121voto

AndyG Points 3298

Comme @Rakete dit dans leur excellente réponse, c'est délicat. Je voudrais ajouter à cela un peu.

L'opérateur ternaire doit avoir la forme:

logique-ou-expression ? expression : cession d'expression

Nous avons donc les mappages suivants:

  • someValue : logique-ou-expression
  • ++x, ++y : l'expression
  • ??? est à l'affectation de l'expression --x, --y ou seulement --x?

En fait c'est l' --x , car une expression d'affectation ne peut pas être analysé comme deux expressions séparées par une virgule (en fonction de C++de règles de grammaire), de sorte --x, --y ne peut pas être considérée comme une expression d'affectation.

Qui résultats dans le ternaire (conditionnel) expression de la partie pour ressembler à ceci:

someValue?++x,++y:--x

Il peut aider pour des raisons de lisibilité du souci à considérer ++x,++y calculé comme si, entre parenthèses (++x,++y); tout ce qui est contenu entre ? et : sera séquencé après le conditionnel. (Je vais parenthesize pour le reste de la poste).

et évaluées dans cet ordre:

  1. someValue?
  2. (++x,++y) ou --x (selon boolsuite à: 1.)

Cette expression est ensuite traitée comme la gauche de la sous-expression d'un opérateur virgule, avec le droit de sous-expression --y, comme suit:

(someValue?(++x,++y):--x), --y;

Ce qui signifie que le côté gauche est un rebut de la valeur de l'expression, ce qui signifie qu'il est certainement évaluée, mais ensuite, nous évaluons le côté droit et de retour que.

Donc ce qui arrive quand someValue est true?

  1. (someValue?(++x,++y):--x) s'exécute et s'incrémente x et y être 11 et 11
  2. La gauche expression est jeté (bien que les effets secondaires de l'incrément de rester)
  3. Nous évaluons le côté droit de l'opérateur virgule: --y, qui puis décrémente y de retour à l' 10

Pour "résoudre" le problème, vous pouvez regrouper --x, --y avec des parenthèses pour la transformer en une expression primaire qui est une entrée valide pour une cession-expression*:

someValue?++x,++y:(--x, --y);

*C'est plutôt drôle à longue chaîne qui relie une affectation expression de revenir à une expression primaire:

cession-expression ---(peut consister)--> conditionnel expression --> logique-ou-expression --> logique et expression --> inclusive-ou-expression --> ou-exclusif-l'expression --> et expression --> l'égalité d'expression --> relationnelle expression --> décalage expression --> additif expression --> multiplicative-expression --> pm-expression --> fonte d'expression --> unaire-expression --> postfix-expression --> primary-expression

87voto

Rakete1111 Points 10248

Wow, c'est délicat.

Le compilateur voit votre expression comme:

(someValue ? (++x, ++y) : --x), --y;

L'opérateur ternaire besoin d'un :, il ne peut pas par lui-même dans ce contexte, mais après cela, il n'ya aucune raison pourquoi la virgule doivent appartenir à la fausse affaire.

Maintenant, il pourrait faire plus de sens pourquoi vous obtenez cette sortie. Si someValue est vrai, alors ++x, ++y et --y exécuté, ce qui ne veut pas changer efficacement y mais en ajoute une x.

Si someValue est faux, alors --x et --y sont exécutées, la décrémentation d'eux à la fois par un.

42voto

dasblinkenlight Points 264350

Pourquoi le compilateur C++ de générer le code que pour le vrai-direction générale de l'opérateur ternaire seulement incréments x

Vous avez mal interprété ce qui s'est passé. Le vrai-direction des incréments de deux x et y. Toutefois, y est décrémenté immédiatement après, sans condition.

Voici comment cela se passe: depuis l'opérateur conditionnel a une priorité plus élevée que l'opérateur virgule en C++, le compilateur analyse de l'expression comme suit:

   (someValue ? ++x, ++y : --x), (--y);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^  ^^^^^

Remarque les "orphelins" --y après la virgule. C'est ce qui mène à la décrémentation y qui a été initialement incrémenté.

Je suis même allé jusqu'à mettre des parenthèses autour de la vraie-branche comme ceci:

someValue ? (++x, ++y) : --x, --y;

Vous étiez sur la bonne voie, mais vous avez mis entre parenthèses une mauvaise direction: vous pouvez résoudre ce problème en parenthesizing l'autre branche, comme ceci:

someValue ? ++x, ++y : (--x, --y);

Démo (tirages 11 11)

5voto

Martin Bonner Points 91

Votre problème est que le ternaire expression n'a pas vraiment une priorité plus élevée que la virgule. En fait, le C++ ne peut pas être décrite avec précision, simplement en priorité - et c'est précisément l'interaction entre l'opérateur ternaire et la virgule où il se casse.

a ? b++, c++ : d++

est considérée comme:

a ? (b++, c++) : d++

(la virgule se comporte comme si elle a une priorité plus élevée). D'autre part,

a ? b++ : c++, d++

est considérée comme:

(a ? b++ : c++), d++

et l'opérateur ternaire est une priorité plus élevée.

2voto

Tarryn Points 644

Un point qui a été négligé dans les réponses (bien que touché des commentaires), c'est que l'opérateur conditionnel est toujours utilisée (prévu par le design?) dans le code réel, comme un raccourci pour l'attribution de l'une des deux valeurs à une variable.

Ainsi, le contexte plus large serait:

whatIreallyWanted = someValue ? ++x, ++y : --x, --y;

Ce qui est absurde sur son visage, de sorte que les crimes sont multiples:

  • Le langage permet ridicule effets secondaires dans une tâche.
  • Le compilateur ne vous avais pas prévenu que vous faisiez des choses bizarres.
  • Le livre semble être en se concentrant sur le "truc" des questions. On ne peut qu'espérer que la réponse dans le dos, ont été "ce Que cette expression n'est dépendent de l'étrange cas de bord dans un exemple artificiel à produire des effets secondaires que personne ne s'attend. Ne jamais faire cela."

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X