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 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 en fait été inspirée par une faute de frappe dans le code. Ce qui était censé être
a = b;
c = d;
Transformé en
a = b, // <- Note comma typo!
c = d;
c++
comma-operator
Joe Schneider
la source
la source
a = (b, c);
.a = b, c = d;
réellement comme prévua = b; c = d;
?b
etd
sont des évaluations de fonctions qui utilisent (et modifient) un état commun, l'ordre d'exécution n'est défini queC++17
.Réponses:
Ce serait égal à
b
.L'opérateur virgule a une priorité inférieure à l'affectation.
la source
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 assez intelligemment l'opérateur virgule pour implémenter des initialiseurs de liste pour les tables de symboles. Ainsi, cela rend la syntaxe suivante possible et significative:
Notez qu'en raison de la priorité des opérateurs, le code est (intentionnellement!) Identique à
Autrement dit, le premier opérateur appelé est
keywords.operator =("and")
qui renvoie un objet proxy sur lequel lesoperator,
s restants sont invoqués:la source
char[]
, qui ne peut pas être surchargé. Le code appelle intentionnellement d' abord leoperator=
, puisoperator,
pour chaque élément restant.L'opérateur virgule a la priorité la plus basse de tous les opérateurs C / C ++. C'est donc toujours le dernier à se lier à une expression, c'est-à-dire ceci:
est équivalent à:
Un autre fait intéressant est que l'opérateur virgule introduit un point de séquence . Cela signifie que l'expression:
est garanti d'avoir ses trois sous-expressions ( a + b , c () et d ) évaluées dans l'ordre. Ceci est important s'ils ont des effets secondaires. Normalement, les compilateurs sont autorisés à évaluer les sous-expressions dans l'ordre de leur choix; par exemple, dans un appel de fonction:
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.
la source
,
a une priorité si faible, il est même en retard sur lui-même ;) ... C'est-à-dire: l' opérateur virgule-comme- a une priorité inférieure à la virgule-comme- séparateur . Donc, si vous souhaitez utiliser une virgule comme opérateur dans un seul argument de fonction, une affectation de variable ou une autre liste séparée par des virgules, vous devez utiliser des parenthèses, par exemple:int a = 1, b = 2, weirdVariable = (++a, b), d = 4;
L'opérateur virgule:
Une version par défaut de l'opérateur virgule est définie pour tous les types (intégrés et personnalisés) et fonctionne comme suit - étant donné
exprA , exprB
:exprA
est évaluéexprA
est ignoréexprB
est évaluéexprB
est renvoyé comme le résultat de l'expression entièreAvec 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()
il sautera 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 virgule conserve toutes comme une seule expression. C'est pourquoi l'opérateur virgule est parfois utilisé dans les scénarios suivants:
if( HERE )
for
bouclefor ( HERE ; ; )
if (foo) HERE ;
(s'il vous plaît ne faites 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 ne sont pas autorisés:
(foo, if (foo) bar)
(if
n'est pas une expression)Dans votre cas, nous avons:
a=b, c;
, équivalent àa=b; c;
, en supposant qu'ila
est d'un type qui ne surcharge pas l'opérateur virgule.a = b, c = d;
équivalent àa=b; c=d;
, en supposant qu'ila
est d'un type qui ne surcharge pas l'opérateur virgule.Notez que toutes les virgules ne sont pas en fait un opérateur 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 par virguleint a=5, b=3;
--- c'est aussi une liste de déclarations de variables séparées par des virgulesfoo(x,y)
--- Liste d'arguments séparés par des virgules. En fait,x
ety
peut être évalué dans n'importe quel ordre!FOO(x,y)
--- Liste d'arguments de macro séparés par des virgulesfoo<a,b>
--- Liste d'arguments de modèle séparés par des virgulesint foo(int a, int b)
--- Liste de paramètres séparés par des virgulesFoo::Foo() : a(5), b(3) {}
--- Liste d'initialiseurs séparés par des virgules dans un constructeur de classe* Ce n'est pas entièrement 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.
Lectures complémentaires: http://en.wikipedia.org/wiki/Comma_operator
la source
operator ,
est surchargé, vous perdez toutes les garanties d'associativité (tout comme vous perdez les propriétés de court-circuit duoperator&&
etoperator||
s'ils sont surchargés)?a, b, c
signifie toujours(a, b), c
et jamaisa, (b, c)
. Cette dernière interprétation pourrait même conduire à une erreur de compilation si les éléments sont de types différents. Qu'est-ce que vous pouvez être après est l'ordre d'évaluation des arguments? Je ne suis pas sûr de cela, mais peut-être avez-vous raison: il peut arriver que cec
soit évalué avant(a, b)
même si la virgule est associative à gauche.struct Foo { Foo() : a(5), b(3) {} int b; int a; }
éviteb(3)
avanta(5)
. Ceci est important si votre liste est comme ceci:Foo() : a(5), b(a) {}
. b ne sera pas défini sur 5, mais plutôt sur la valeur non initialisée de a, que votre compilateur peut ou non avertir.La valeur de
a
serab
, mais la valeur de l'expression serac
. C'est dedansa serait égal à
b
etd
serait égal àc
.la source
a = b; d = c;
?La valeur de b sera attribuée à a. Rien n'arrivera à c
la source
La valeur de a sera égale à b, car l'opérateur virgule a une priorité inférieure à l'opérateur d'affectation.
la source
Oui L'opérateur virgule a une priorité inférieure à l'opérateur d'affectation
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:
Ouput: i = 1
Comme nous le savons, l'opérateur virgule a une priorité inférieure à l'affectation .....
la source
i = 1;
sur cette ligne?Tout d'abord: la virgule n'est en fait pas un opérateur, pour le compilateur c'est juste un jeton qui prend une signification 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:
Habituellement, un débutant en C ++ penserait que cette expression pourrait / voudrait comparer les choses, mais c'est absolument faux, la signification du
<
,>
et,
jetons dépendent du contexte d'utilisation.L'interprétation correcte de l'exemple ci-dessus est bien sûr qu'il s'agit d'une instatiation d'un modèle.
Exemple 2:
Lorsque nous écrivons une boucle typiquement for avec plus d'une variable d'initialisation et / ou plusieurs expressions qui devraient être faites après chaque itération de la boucle, nous utilisons également la virgule:
La signification de la virgule dépend du contexte d'utilisation, ici c'est le contexte du
for
construction.Que signifie réellement une virgule dans le contexte?
Pour le compliquer encore plus (comme toujours en C ++), l'opérateur virgule peut lui-même être surchargé (grâce à Konrad Rudolph pour l'avoir signalé).
Pour revenir à la question, le Code
signifie pour le compilateur quelque chose comme
car la priorité du
=
jeton / opérateur est supérieure à la priorité du,
jeton.et ceci est interprété dans un contexte comme
(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.)
la source