Come funziona l'operatore virgola


175

Come funziona l'operatore virgola in C ++?

Ad esempio, se lo faccio:

a = b, c;  

Un finisce per eguagliare c?

(Sì, lo so che è facile da testare, basta documentare qui per qualcuno per trovare rapidamente la risposta.)

Aggiornamento: questa domanda ha esposto una sfumatura quando si utilizza l'operatore virgola. Giusto per documentare questo:

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

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

Questa domanda è stata in realtà ispirata da un refuso nel codice. Quello che doveva essere

a = b;
c = d;

Diventato

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


1
Possibile duplicato di Cosa fa l'operatore virgola `,` in C? . Ti ha battuto un giorno. E la risposta di Lillq fornisce una risposta alla domanda su a = (b, c);.
jww

5
Ma in questo caso a = b, c = d;funziona esattamente come previsto a = b; c = d;?
Bondolin,

@NargothBond Non necessariamente. Se be dsono valutazioni di funzioni che utilizzano (e modificano) uno stato comune, l'ordine di esecuzione non è definito fino a C++17.
nyronium,

Risposte:


74

Sarebbe uguale a b.

L'operatore virgola ha una precedenza inferiore rispetto all'assegnazione.


129

Prestare attenzione a notare che l'operatore virgola potrebbe essere sovraccarico in C ++. Il comportamento reale può quindi essere molto diverso da quello atteso.

Ad esempio, Boost.Spirit utilizza l'operatore virgola in modo abbastanza intelligente per implementare gli inizializzatori di elenchi per le tabelle dei simboli. Pertanto, rende possibile e significativa la seguente sintassi:

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

Si noti che a causa della precedenza dell'operatore, il codice è (intenzionalmente!) Identico a

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

Cioè, il primo operatore chiamato è keywords.operator =("and")che restituisce un oggetto proxy su cui operator,sono invocati i rimanenti s:

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

Umm, non puoi cambiare la precedenza però, il che significa che probabilmente dovresti mettere parentesi nella tua lista.
Jeff Burdges,

18
@Jeff Al contrario. Con una parentesi nell'elenco, questo non funzionerebbe da allora il compilatore vede solo l'operatore virgola tra due char[], che non può essere sovraccaricato. Il codice prima chiama intenzionalmenteoperator= e poi operator,per ciascun elemento rimanente.
Konrad Rudolph,

125

L'operatore virgola ha la precedenza più bassa di tutti gli operatori C / C ++. Pertanto è sempre l'ultimo a legarsi a un'espressione, nel senso che:

a = b, c;

è equivalente a:

(a = b), c;

Un altro fatto interessante è che l'operatore virgola introduce un punto sequenza . Ciò significa che l'espressione:

a+b, c(), d

è garantito che le sue tre sottoespressioni ( a + b , c () e d ) siano valutate in ordine. Ciò è significativo se hanno effetti collaterali. Normalmente i compilatori possono valutare le sottoespressioni nell'ordine che ritengono opportuno; ad esempio, in una chiamata di funzione:

someFunc(arg1, arg2, arg3)

gli argomenti possono essere valutati in un ordine arbitrario. Si noti che le virgole nella chiamata di funzione non sono operatori; sono separatori.


15
Vale la pena sottolineare che ,ha una precedenza così bassa, è addirittura in ritardo rispetto a se stesso ;) ... Cioè: la virgola come operatore ha una precedenza inferiore rispetto alla virgola come separatore . Quindi, se si desidera utilizzare la virgola come operatore all'interno di un singolo argomento di funzione, assegnazione di variabile o altro elenco separato da virgole, è necessario utilizzare le parentesi, ad esempio:int a = 1, b = 2, weirdVariable = (++a, b), d = 4;
underscore_d

68

L'operatore virgola:

  • ha la precedenza più bassa
  • è associativo di sinistra

Una versione predefinita dell'operatore virgola è definita per tutti i tipi (integrati e personalizzati) e funziona come segue - dato exprA , exprB:

  • exprA viene valutato
  • il risultato di exprA viene ignorato
  • exprB viene valutato
  • il risultato di exprBviene restituito come risultato dell'intera espressione

Con la maggior parte degli operatori, al compilatore è consentito scegliere l'ordine di esecuzione ed è persino necessario saltare l'esecuzione se non influisce sul risultato finale (ad esempio false && foo()salterà la chiamata a foo). Questo non è tuttavia il caso dell'operatore virgola e i passaggi precedenti accadranno sempre * .

In pratica, l'operatore virgola predefinito funziona quasi allo stesso modo di un punto e virgola. La differenza è che due espressioni separate da un punto e virgola formano due istruzioni separate, mentre la separazione delle virgole mantiene tutte come un'unica espressione. Questo è il motivo per cui l'operatore virgola viene talvolta utilizzato nei seguenti scenari:

  • La sintassi C richiede una singola espressione , non un'istruzione. ad esif( HERE )
  • La sintassi C richiede una singola istruzione, non di più, ad esempio nell'inizializzazione del forloopfor ( HERE ; ; )
  • Quando vuoi saltare le parentesi graffe e mantenere una singola affermazione: if (foo) HERE ;(per favore non farlo, è davvero brutto!)

Quando un'istruzione non è un'espressione, il punto e virgola non può essere sostituito da una virgola. Ad esempio, questi non sono consentiti:

  • (foo, if (foo) bar)( ifnon è un'espressione)
  • int x, int y (la dichiarazione di variabile non è un'espressione)

Nel tuo caso abbiamo:

  • a=b, c;, equivalente a a=b; c;, supponendo che asia di tipo che non sovraccarichi l'operatore virgola.
  • a = b, c = d;equivalente a a=b; c=d;, supponendo che asia di tipo che non sovraccarichi l'operatore virgola.

Nota che non tutte le virgole sono in realtà un operatore virgola. Alcune virgole che hanno un significato completamente diverso:

  • int a, b; --- l'elenco delle dichiarazioni delle variabili è separato da virgola, ma questi non sono operatori virgola
  • int a=5, b=3; --- questo è anche un elenco di dichiarazioni di variabili separate da virgola
  • foo(x,y)--- Elenco di argomenti separati da virgola. In effetti, xe ypuò essere valutato in qualsiasi ordine!
  • FOO(x,y) --- Elenco di argomenti macro separati da virgola
  • foo<a,b> --- Elenco di argomenti del modello separati da virgola
  • int foo(int a, int b) --- Elenco dei parametri separati da virgola
  • Foo::Foo() : a(5), b(3) {} --- elenco di inizializzatori separati da virgole in un costruttore di classi

* Questo non è del tutto vero se si applicano ottimizzazioni. Se il compilatore riconosce che un determinato codice non ha assolutamente alcun impatto sul resto, rimuoverà le dichiarazioni non necessarie.

Ulteriori letture: http://en.wikipedia.org/wiki/Comma_operator


Vale la pena notare che se il operator ,sovraccarico perde qualche garanzia sull'associatività (proprio come si perdono le proprietà di corto circuito del operator&&e operator||se sono sovraccarichi)?
Young John,

L'operatore virgola è associativo di sinistra indipendentemente dal fatto che sia sovraccarico o meno. Un'espressione a, b, csignifica sempre (a, b), ce mai a, (b, c). Quest'ultima interpretazione potrebbe persino portare a errori di compilazione se gli elementi sono di tipi diversi. Cosa potresti fare dopo l'ordine di valutazione degli argomenti? Non ne sono sicuro, ma forse hai ragione: potrebbe succedere che cvenga valutato prima (a, b) anche se la virgola è associativa di sinistra.
CygnusX1,

1
Solo un leggero commento sull'elenco di inizializzazione separato da virgole in un costruttore di classi, l'ordine non è determinato dalla posizione nell'elenco. L'ordine è determinato dalla posizione della dichiarazione della classe. Ad esempio struct Foo { Foo() : a(5), b(3) {} int b; int a; }evaula b(3)prima a(5). Questo è importante se l'elenco è in questo modo: Foo() : a(5), b(a) {}. b non sarà impostato su 5, ma piuttosto sul valore non inizializzato di a, di cui il compilatore potrebbe o meno avvertire.
Jonathan Gawrych,

Di recente mi sono imbattuto in un operatore virgola con due float, a che serve valutare e scartare un numero?
Aaron Franke,

Non penso che nessuno possa rispondere. Dovresti mostrarlo in un contesto. Probabilmente una domanda separata?
CygnusX1,

38

Il valore asarà b, ma il valore dell'espressione sarà c. Cioè, in

d = (a = b, c);

a sarebbe uguale a be dsarebbe uguale a c.


19
Quasi corretto Le dichiarazioni non hanno valori, le espressioni sì. Il valore di quell'espressione è c.
Leon Timmermans,

Perché viene utilizzato al posto di a = b; d = c;?
Aaron Franke,

Questo mi ha fatto capire di quali effetti collaterali stavano parlando le persone.
lacci delle scarpe

8

il valore di b sarà assegnato a a. Non succederà nulla a c


2

Il valore di a sarà uguale a b, poiché l'operatore virgola ha una precedenza inferiore rispetto all'operatore di assegnazione.


2

Sì L'operatore virgola ha una precedenza bassa rispetto all'operatore assegnazione

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

Output: i = 3
Perché l'operatore virgola restituisce sempre il valore più a destra.
In caso di operatore virgola con Operatore assegnazione:

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

Ouput: i = 1
Come sappiamo, l'operatore virgola ha una precedenza inferiore rispetto all'assegnazione .....


Quindi, in che modo il secondo esempio è diverso dal solo avere i = 1;su quella linea?
Aaron Franke,

-3

Per prima cosa: la virgola non è in realtà un operatore, per il compilatore è solo un token che ha un significato nel contesto di altri token.

Cosa significa questo e perché preoccuparsi?

Esempio 1:

Per comprendere la differenza tra il significato dello stesso token in un contesto diverso, diamo un'occhiata a questo esempio:

class Example {
   Foo<int, char*> ContentA;
}

Di solito un principiante C ++ penserebbe che questa espressione potrebbe / confronterebbe le cose ma è assolutamente sbagliato il significato di <, >e, gettoni depent sul contesto d'uso.

L'interpretazione corretta dell'esempio sopra riportato è ovviamente che si tratta di un'installazione di un modello.

Esempio 2:

Quando scriviamo un ciclo tipicamente for con più di una variabile di inizializzazione e / o più espressioni che dovrebbero essere fatte dopo ogni iterazione del ciclo usiamo anche la virgola:

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

Il significato della virgola dipende dal contesto di utilizzo, qui è il contesto di for costruzione.

Cosa significa effettivamente una virgola nel contesto?

A complicarlo ancora di più (come sempre in C ++) l'operatore virgola può essere sovraccaricato (grazie a Konrad Rudolph per averlo sottolineato).

Per tornare alla domanda, il Codice

a = b, c;

significa per il compilatore qualcosa di simile

(a = b), c;

perché la priorità del =token / operatore è superiore alla priorità di, token.

e questo è interpretato in un contesto simile

a = b;
c;

(nota che l'interpretazione dipende dal contesto, qui non è né una chiamata di funzione / metodo né un'istatizzazione di modello.)


1
certo che lo è, forse ho usato una terminologia sbagliata (per il lexer è un token, certo)
Quonux,

2
Come si lavora con l' operatore, (sic), la virgola è davvero un operatore.
DragonLord,

2
Mentre riconoscere se un determinato token di virgola è riconosciuto come operatore di virgola (al contrario, ad esempio, di separatore di argomenti) può essere una sfida di per sé, questa domanda riguarda specificamente l' operatore di virgola .
CygnusX1
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.