Comment fonctionne L'opérateur virgule
Comment fonctionne l'opérateur virgule en C++?
Par exemple, si je 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 la réponse rapidement.)
Mise à Jour: Cette question a exposé 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. Ce qui était censé être
a = b;
c = d;
Tourné dans
a = b, // <- Note comma typo!
c = d;
9 réponses
, Il serait égal à b
.
L'opérateur virgule a une priorité inférieure à l'affectation.
Prenez soin de noter que l'opérateur 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 des initialiseurs de liste pour les tables de symboles. Ainsi, il rend la syntaxe suivante possible et significative:
keywords = "and", "or", "not", "xor";
Notez qu'en raison de la priorité de l'opérateur, le code est (intentionnellement!) identique à
(((keywords = "and"), "or"), "not"), "xor";
C'est-à-dire le premier opérateur appelé est {[3] } qui renvoie un objet proxy sur lequel les operator,
restants sont invoqués:
keywords.operator =("and").operator ,("or").operator ,("not").operator ,("xor");
L'opérateur virgule A la priorité la plus basse 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 virgule introduit un point de séquence . Cela signifie que l'expression:
a+b, c(), d
Est la garantie d'avoir trois de ses sous-expressions (a+b, c() et d) évaluées dans l'ordre. Ceci est significatif si ils ont des effets secondaires. Normalement, les compilateurs sont autorisés à évaluer les sous-expressions dans n'importe quel ordre; 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 sont des opérateurs Pas ; Ce sont des séparateurs.
L'opérateur virgule:
- a la priorité la plus basse
- est associatif à gauche
Une version par défaut de l'opérateur virgule est définie pour tous les types (intégré et personnalisé), et il fonctionne comme suit-donné exprA , exprB
:
- {[1] } est évalué
- le résultat de
exprA
est ignoré - {[3] } est évalué
- le résultat de
exprB
est retournée comme résultat de l'ensemble de l'expression
Avec la plupart des opérateurs, le compilateur est autorisé à choisir l'ordre d'exécution et il est même nécessaire d'ignorer l'exécution si elle n'affecte pas le résultat final (par exemple {[5] } ignorera l'appel à foo
). Ce n'est cependant pas le cas pour l'opérateur virgule et les étapes ci dessus se produiront toujours*.
En pratique, l'opérateur virgule par défaut fonctionne presque de la même manière qu'un point-virgule. La différence est que deux expressions séparées par un point-virgule forment deux instructions distinctes, tandis que la séparation par des virgules conserve tout comme une seule expression. C'est pourquoi l'opérateur virgule est parfois utilisé dans les scénarios suivants:
- la syntaxe C nécessite une seule expression , pas une instruction. par exemple dans
if( HERE )
- la syntaxe C nécessite une seule instruction, pas plus, par exemple dans l'initialisation de la boucle
for
for ( HERE ; ; )
- quand vous voulez sauter des accolades et garder une seule déclaration:
if (foo) HERE ;
(ne faites pas ça, c'est vraiment moche!)
Lorsqu'une déclaration n'est pas expression, point-virgule ne peut pas être remplacé par une virgule. Par exemple, ceux-ci sont refusés:
-
(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;
, en supposant quea
est de type qui ne surcharge pas l'opérateur virgule. -
a = b, c = d;
équivalent àa=b; c=d;
, en supposant quea
est de type qui ne surcharge pas la virgule opérateur.
Notez que toutes les virgules ne sont pas en fait un opérateur de virgule. Quelques virgules qui ont une signification complètement différente:
-
int a, b;
- - - la liste des déclarations de variables 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éclarations de variables séparées par des virgules -
foo(x,y)
- - - liste d'arguments séparés par des virgules. En fait,x
ety
peuvent être évalués dans n'importe quel ordre! -
FOO(x,y)
--- liste d'arguments de macro séparés par des virgules -
foo<a,b>
- - - liste d'arguments de modèle séparés par des virgules -
int foo(int a, int b)
- - - liste de paramètres séparés par des virgules -
Foo::Foo() : a(5), b(3) {}
- - - liste d'initialiseurs séparés par des virgules dans un constructeur de classe
* Ce 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 reste, il supprimera les instructions inutiles.
Autres lectures: http://en.wikipedia.org/wiki/Comma_operator
La valeur de a
est b
, mais la valeur de l'expression sera c
. C'est, dans
d = (a = b, c);
A est égal à b
, et d
est égal à c
.
La valeur de a sera égale à b, puisque l'opérateur virgule a une priorité inférieure à l'opérateur d'affectation.
Oui L'opérateur virgule a une faible priorité que l'opérateur D'affectation
#include<stdio.h>
int main()
{
int i;
i = (1,2,3);
printf("i:%d\n",i);
return 0;
}
Sortie: i = 3
Parce que l'opérateur 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;
}
Sortie: i = 1
Comme nous le savons, l'opérateur de virgule a une priorité inférieure à l'affectation.....
Tout d'abord: la virgule n'est en fait pas un opérateur, pour le compilateur c'est juste un jeton qui a une signification dans le 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 jetons un coup d'œil à cet exemple:
class Example {
Foo<int, char*> ContentA;
}
Habituellement, un débutant en C++ penserait que cette expression pourrait / pourrait comparer les choses mais il est absolument faux, le sens de l'<
, >
et les jetons ,
dépendent du contexte d'utilisation.
L'interprétation correcte de l'exemple ci-dessus, c'est qu'il est un instatiation d'un modèle.
Exemple 2:
Lorsque nous écrivons une boucle typiquement for avec plus d'une variable d'initialisation et/ou plus d'une expression qui devrait être faite après chaque itération de la boucle, nous utilisons aussi 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 for
construction.
Qu'est une virgule dans le contexte signifie réellement?
Pour compliquer encore plus (comme toujours en C++) de l'opérateur virgule peut lui-même être surchargé (merci à Konrad Rudolph pour souligné).
Pour revenir à la question, le Code
a = b, c;
Signifie pour le compilateur quelque chose comme
(a = b), c;
Parce que la priorité du jeton/Opérateur =
est supérieur à la priorité du jeton ,
.
Et ceci est interprété dans le contexte comme
a = b;
c;
(notez que l'interprétation dépend du contexte, ici il ne s'agit ni d'un appel de fonction / méthode ni d'une instatiation de modèle.)