Comment fonctionne l’opérateur de virgule

Comment fonctionne l’opérateur de virgule en C ++?

Par exemple, si je le fais:

a = b, c; 

Est-ce que ça finit par égaler b ou c?

(Oui, je sais que c’est facile à tester – il suffit de documenter ici pour que quelqu’un trouve rapidement la réponse.)

Mise à jour: Cette question a révélé une nuance lors de l’utilisation de l’opérateur virgule. Juste pour documenter ceci:

 a = b, c; // a is set to the value of b! a = (b, c); // a is set to the value of c! 

Cette question a été inspirée par une faute de frappe dans le code. Quel était l’intention d’être

 a = b; c = d; 

Transformé en

 a = b, // <- Note comma typo! c = d; 

Ce serait égal à b .

L’opérateur de virgule a une priorité inférieure à l’affectation.

Notez que l’opérateur de virgule peut être surchargé en C ++. Le comportement réel peut donc être très différent de celui attendu.

Par exemple, Boost.Spirit utilise l’opérateur virgule assez intelligemment pour implémenter les initialiseurs de liste pour les tables de symboles. Ainsi, la syntaxe suivante est possible et significative:

 keywords = "and", "or", "not", "xor"; 

Notez qu’en raison de la priorité des opérateurs, le code est (intentionnellement!) Identique à

 (((keywords = "and"), "or"), "not"), "xor"; 

En d’autres termes, le premier opérateur appelé keywords.operator =("and") renvoie un object proxy sur lequel l’ operator, restant operator, s operator, est appelé:

 keywords.operator =("and").operator ,("or").operator ,("not").operator ,("xor"); 

L’opérateur virgule a la plus faible priorité de tous les opérateurs C / C ++. Par conséquent, c’est toujours le dernier à se lier à une expression, ce qui signifie ceci:

 a = b, c; 

est équivalent à:

 (a = b), c; 

Un autre fait intéressant est que l’opérateur de virgule introduit un sharepoint séquence . Cela signifie que l’expression:

 a+b, c(), d 

est garanti d’avoir ses trois sous-expressions ( a + b , c () et d ) évaluées dans l’ordre. Ceci est significatif s’ils ont des effets secondaires. Normalement, les compilateurs sont autorisés à évaluer les sous-expressions dans l’ordre qu’ils trouvent approprié; Par exemple, dans un appel de fonction:

 someFunc(arg1, arg2, arg3) 

les arguments peuvent être évalués dans un ordre arbitraire. Notez que les virgules dans l’appel de fonction ne sont pas des opérateurs; ce sont des séparateurs.

L’opérateur virgule:

  • a la préséance la plus basse
  • est associatif à gauche

Une version par défaut de l’opérateur de virgule est définie pour tous les types (intégré et personnalisé), et fonctionne comme suit – avec exprA , exprB :

  • exprA est évalué
  • le résultat de exprA est ignoré
  • exprB est évalué
  • le résultat de exprB est renvoyé comme résultat de l’expression entière

Avec la plupart des opérateurs, le compilateur est autorisé à choisir l’ordre d’exécution et il est même nécessaire de sauter l’exécution si cela n’affecte pas le résultat final (par exemple, false && foo() ignorera l’appel à foo ). Ce n’est toutefois pas le cas pour l’opérateur de virgule et les étapes ci-dessus se produiront toujours * .

En pratique, l’opérateur de virgule par défaut fonctionne presque comme un point-virgule. La différence réside dans le fait que deux expressions séparées par un point-virgule forment deux instructions distinctes, tandis que la séparation des virgules conserve tout en une seule expression. C’est pourquoi l’opérateur de virgule est parfois utilisé dans les scénarios suivants:

  • La syntaxe C nécessite une seule expression , pas une déclaration. par exemple dans if( HERE )
  • La syntaxe C nécessite une seule instruction, pas plus, par exemple lors de l’initialisation de la boucle for ( HERE ; ; )
  • Lorsque vous voulez ignorer les accolades et conserver une seule instruction: if (foo) HERE ; (ne fais pas ça, c’est vraiment moche!)

Lorsqu’une instruction n’est pas une expression, le point-virgule ne peut pas être remplacé par une virgule. Par exemple, ceux-ci sont interdits:

  • (foo, if (foo) bar) ( if n’est pas une expression)
  • int x, int y (la déclaration de variable n’est pas une expression)

Dans votre cas, nous avons:

  • a=b, c; , équivalent à a=b; c; a=b; c; , en supposant que a est de type qui ne surcharge pas l’opérateur virgule.
  • a = b, c = d; équivalent à a=b; c=d; a=b; c=d; , en supposant que a est de type qui ne surcharge pas l’opérateur virgule.

Notez que toutes les virgules ne sont pas des opérateurs de virgules. Certaines virgules qui ont un sens complètement différent:

  • int a, b; — la liste de déclaration de variable est séparée par des virgules, mais ce ne sont pas des opérateurs de virgule
  • int a=5, b=3; — c’est aussi une liste de déclaration de variables séparées par des virgules
  • foo(x,y) — liste d’arguments séparés par des virgules. En fait, x et y peuvent être évalués dans n’importe quel ordre!
  • FOO(x,y) — liste d’arguments de macro séparés par des virgules
  • foo — liste d’arguments de modèle séparés par des virgules
  • int foo(int a, int b) — liste de parameters séparés par des virgules
  • Foo::Foo() : a(5), b(3) {} — liste d’initialisation séparée par des virgules dans un constructeur de classes

* Ceci n’est pas tout à fait vrai si vous appliquez des optimisations. Si le compilateur reconnaît que certains morceaux de code n’ont absolument aucun impact sur le rest, il supprimera les instructions inutiles.

Lectures complémentaires: http://en.wikipedia.org/wiki/Comma_operator

La valeur de a sera b , mais la valeur de l’expression sera c . C’est dedans

 d = (a = b, c); 

a serait égal à b , et d serait égal à c .

La valeur de b sera assignée à a. Rien n’arrivera à c

La valeur de a sera égale à b, car l’opérateur de virgule a une priorité inférieure à l’opérateur d’affectation.

Oui L’opérateur de virgule est peu prioritaire par rapport à l’opérateur d’affectation

 #include int main() { int i; i = (1,2,3); printf("i:%d\n",i); return 0; } 

Sortie: i = 3
Parce que l’opérateur de virgule renvoie toujours la valeur la plus à droite.
En cas d’opérateur virgule avec opérateur d’affectation:

  int main() { int i; i = 1,2,3; printf("i:%d\n",i); return 0; } 

Ouput: i = 1
Comme nous le soaps, l’opérateur de virgule a moins de priorité que l’affectation …..

Tout d’abord: Comma n’est en fait pas un opérateur, pour le compilateur c’est juste un jeton qui prend un sens en contexte avec d’autres jetons.

Qu’est-ce que cela signifie et pourquoi s’embêter?

Exemple 1:

Pour comprendre la différence entre la signification du même jeton dans un contexte différent, nous examinons cet exemple:

 class Example { Foo ContentA; } 

Habituellement, un débutant en C ++ penserait que cette expression pourrait / pourrait comparer des choses mais il est absolument faux, la signification des jetons < , > et , dépend du contexte d'utilisation.

L'interprétation correcte de l'exemple ci-dessus est bien sûr que c'est une instatiation d'un modèle.

Exemple 2:

Lorsque nous écrivons une boucle typiquement pour plus d'une variable d'initialisation et / ou plusieurs expressions à faire après chaque itération de la boucle, nous utilisons également des virgules:

 for(a=5,b=0;a<42;a++,b--) ... 

La signification de la virgule dépend du contexte d'utilisation, ici c'est le contexte de la construction.

Que signifie une virgule dans son contexte?

Pour le compliquer encore plus (comme toujours en C ++), l'opérateur de virgule peut lui-même être surchargé (merci à Konrad Rudolph de l' avoir signalé).

Pour revenir à la question, le code

 a = b, c; 

signifie pour le compilateur quelque chose comme

 (a = b), c; 

car la priorité de l’opérateur = est plus élevée que la priorité du jeton,.

et cela est interprété dans un contexte comme

 a = b; c; 

(notez que l'interprétation dépend du contexte, ici ce n'est ni un appel de fonction / méthode ni une instatiation de modèle).