186 votes

Comment fonctionne l'opérateur virgule

Comment fonctionne l'opérateur virgule en C++ ?

Par exemple, si je le fais :

a = b, c;  

Est-ce que a finit par être égal à b ou c ?

(Oui, je sais que c'est facile à tester - je le documente juste ici pour que quelqu'un puisse trouver la réponse rapidement).

Mise à jour : Cette question a mis en évidence une nuance dans l'utilisation de l'opérateur virgule. Juste pour documenter cela :

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 devait être

a = b;
c = d;

Transformé en

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

0 votes

Pour en savoir plus, cliquez ici. stackoverflow.com/questions/12824378/

1 votes

Duplicata possible de Que fait l'opérateur virgule `,` en C ? . Il vous a devancé d'un jour. Et la réponse de lillq permet de répondre à la question sur a = (b, c); .

5 votes

Mais dans ce cas a = b, c = d; a effectivement les mêmes performances que celles prévues a = b; c = d; ?

133voto

Konrad Rudolph Points 231505

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

A titre d'exemple, Boost.Spirit utilise l'opérateur virgule de manière très intelligente pour mettre en œuvre des initialisateurs de liste pour les tables de symboles. Ainsi, il rend la syntaxe suivante possible et significative :

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

Remarquez qu'en raison de la précédence des opérateurs, le code est (intentionnellement !) identique à celui de

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

C'est-à-dire que le premier opérateur appelé est keywords.operator =("and") qui renvoie un objet proxy sur lequel les autres operator, sont invoqués :

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

0 votes

Umm, vous ne pouvez cependant pas modifier la préséance, ce qui signifie que vous devriez probablement mettre des parenthèses autour de votre liste.

22 votes

@Jeff Au contraire. Avec une parenthèse autour de la liste, cela ne fonctionnerait pas, car le compilateur ne verrait que l'opérateur virgule entre deux noms. char[] qui ne peut pas être surchargé. Le code intentionnellement appelle d'abord le operator= et par la suite operator, pour chaque élément restant.

129voto

efotinis Points 6338

L'opérateur virgule a le le plus bas la préséance de tous les opérateurs C/C++. Par conséquent, il 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 une point de séquence . Cela signifie que l'expression :

a+b, c(), d

est garantie d'avoir ses trois sous-expressions ( a+b , c() y d ) évalués 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 qu'ils jugent 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 sont no opérateurs ; ce sont des séparateurs.

15 votes

Il convient de souligner que , a une priorité si faible qu'il est même à la traîne derrière mismo ;) ...C'est-à-dire : virgule comme- opérateur a une priorité inférieure à celle de la virgule en tant que séparateur . Donc, si vous voulez utiliser la virgule comme opérateur à l'intérieur d'un seul argument de fonction, d'une affectation de variable ou d'une autre virgule. séparé liste - alors vous devez utiliser des parenthèses, par exemple : int a = 1, b = 2, weirdVariable = (++a, b), d = 4;

77voto

Leon Timmermans Points 23230

Il serait égal à b .

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

73voto

CygnusX1 Points 8578

L'opérateur virgule :

  • a la plus faible priorité
  • est gauche-associatif

Une version par défaut de l'opérateur virgule est définie pour tous les types (intégrés et personnalisés), et elle fonctionne de la manière suivante - donnée exprA , exprB :

  • exprA est évalué
  • le résultat de exprA est ignoré
  • exprB est évalué
  • le résultat de exprB est retourné 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 tenu de sauter l'exécution quelle qu'elle soit si elle n'affecte pas le résultat final (par ex. false && foo() 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 réside dans le fait que deux expressions séparées par un point-virgule forment deux déclarations distinctes, tandis que la séparation par des virgules les conserve comme une seule expression. C'est pourquoi l'opérateur virgule est parfois utilisé dans les scénarios suivants :

  • La syntaxe C nécessite un simple expression et non une déclaration, par exemple dans if( HERE )
  • La syntaxe C ne requiert qu'une seule déclaration, pas plus, par exemple dans l'initialisation de la fonction for boucle for ( HERE ; ; )
  • Lorsque vous voulez éviter les accolades et conserver une seule déclaration : if (foo) HERE ; (s'il vous plaît, ne faites pas ça, c'est vraiment moche !)

Lorsqu'une déclaration 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)
  • int x, int y (la déclaration de la variable n'est pas une expression)

Dans votre cas, nous avons :

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

Notez que toutes les virgules ne sont pas nécessairement des opérateurs de virgule. Certaines virgules 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; --- il s'agit également d'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 y y peut être évaluée dans cualquier commander !
  • 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'initialisateurs 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 qu'un certain morceau de code n'a absolument aucun impact sur le reste, il supprimera les instructions inutiles.

Pour en savoir plus : http://en.wikipedia.org/wiki/Comma_operator

0 votes

Est-il utile de noter que si le operator , est surchargé, vous perdez toute garantie sur l'associativité (tout comme vous perdez les propriétés de court-circuit de la fonction operator&& y operator|| s'ils sont surchargés) ?

0 votes

L'opérateur virgule est associatif à gauche, qu'il soit surchargé ou non. Une expression a, b, c signifie toujours (a, b), c et jamais a, (b, c) . Cette dernière interprétation pourrait même conduire à une erreur de compilation si les éléments sont de types différents.Ce que vous recherchez peut-être, c'est l'ordre d'évaluation des arguments ? Je n'en suis pas sûr, mais vous avez peut-être raison : il pourrait arriver que c est évalué antes de (a, b) même si la virgule est associative à gauche.

3 votes

Juste une petite remarque sur la liste d'initialisation séparée par des virgules dans un constructeur de classe, l'ordre est le suivant no déterminé par la position dans la liste. L'ordre est déterminé par la position de la déclaration de la classe. Par exemple struct Foo { Foo() : a(5), b(3) {} int b; int a; } evaulates b(3) avant a(5) . C'est important si votre liste se présente comme suit : Foo() : a(5), b(a) {} . b ne sera pas fixé à 5, mais plutôt à la valeur non initialisée de a, ce dont votre compilateur peut vous avertir ou non.

40voto

MobyDX Points 365

La valeur de a sera b mais la valeur de l'expression sera c . C'est-à-dire que, dans

d = (a = b, c);

a serait égal à b y d serait égal à c .

19 votes

Presque correct. Les déclarations n'ont pas de valeurs, les expressions en ont. La valeur de cette expression est c.

0 votes

Pourquoi est-ce utilisé au lieu de a = b; d = c; ?

0 votes

Cela m'a permis de comprendre de quels effets secondaires les gens parlaient.

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