Qual è l’uso corretto dell’operatore virgola?

Ho visto questo codice:

if (cond) { perror("an error occurred"), exit(1); } 

Perché dovresti farlo? Perché non solo:

 if (cond) { perror("an error occurred"); exit(1); } 

Nel tuo esempio non ha alcuna ragione. È a volte utile quando scritto come

 if(cond) perror("an error occured"), exit(1) ; 

– allora non hai bisogno di parentesi graffe. Ma è un invito al disastro.

L’operatore virgola deve mettere due o più espressioni in una posizione in cui il riferimento ne consente solo una. Nel tuo caso, non c’è bisogno di usarlo; in altri casi, ad esempio in un ciclo while, può essere utile:

 while (a = b, c < d) ... 

dove la "valutazione" effettiva del ciclo while è regolata esclusivamente sull'ultima espressione.

Casi legittimi dell’operatore virgola sono rari, ma esistono. Un esempio è quando vuoi che succeda qualcosa all’interno di una valutazione condizionale. Per esempio:

 std::wstring example; auto it = example.begin(); while (it = std::find(it, example.end(), L'\\'), it != example.end()) { // Do something to each backslash in `example` } 

Può anche essere utilizzato in luoghi in cui è ansible inserire una sola espressione, ma si desidera che succedano due cose. Ad esempio, il seguente loop incrementa xe decrementa y nel terzo componente del ciclo for:

 int x = 0; int y = some_number; for(; x < y; ++x, --y) { // Do something which uses a converging x and y } 

Non andare in cerca di usi di esso, ma se è appropriato, non aver paura di usarlo, e non essere gettato per un ciclo se vedi qualcun altro che lo usa. Se hai due cose che non hanno motivo di non essere dichiarazioni separate, rendili istruzioni separate invece di usare l'operatore virgola.

L’uso principale dell’operatore virgola è l’offuscamento; permette di fare due cose in cui il lettore si aspetta solo una. Uno degli usi più frequenti, l’aggiunta di effetti collaterali a una condizione, rientra in questa categoria. Ci sono alcuni casi che potrebbero essere considerati validi, tuttavia:

Quello che è stato usato per presentarlo in K & R: incrementando due variabili in un ciclo for . Nel codice moderno, ciò potrebbe verificarsi in una funzione come std::transform o std::copy , in cui un iteratore di output viene incrementato simultaneamente con l’iteratore di input. (Più spesso, ovviamente, queste funzioni conterranno un ciclo while , con le incrementazioni in istruzioni separate alla fine del ciclo: in tali casi, non ha senso usare una virgola anziché due istruzioni.)

Un altro caso che viene in mente è la convalida dei dati dei parametri di input in una lista di inizializzazione:

 MyClass::MyClass( T const& param ) : member( (validate( param ), param) ) { } 

(Questo presuppone che validate( param ) genererà un’eccezione se qualcosa non va.) Questo uso non è particolarmente allettante, specialmente perché ha bisogno delle parentesi aggiuntive, ma non ci sono molte alternative.

Infine, a volte ho visto la convenzione:

 ScopedLock( myMutex ), protectedFunction(); 

, che evita di dover inventare un nome per ScopedLock . A dire il vero, non mi piace, ma l’ho visto usato, e l’alternativa di aggiungere altre parentesi per garantire che lo ScopedLock sia immediatamente distrutto non è molto carina.

Questo può essere meglio compreso prendendo alcuni esempi:

Primo: considera un’espressione:

  x = ++j; 

Ma per il momento, se dobbiamo assegnare un valore di debug temporaneo, allora possiamo scrivere.

  x = DEBUG_VALUE, ++j; 

Secondo:
Virgola , operatori sono frequentemente utilizzati in for() -loop ad esempio:

 for(i = 0, j = 10; i < N; j--, i++) // ^ ^ here we can't use ; 

Terzo:
Un altro esempio (in realtà uno potrebbe trovare interessante questo):

 if (x = 16 / 4), if remainder is zero then print x = x - 1; if (x = 16 / 5), if remainder is zero then print x = x + 1; 

Può anche essere fatto in un unico passaggio;

  if(x = n / d, n % d) // == x = n / d; if(n % d) printf("Remainder not zero, x + 1 = %d", (x + 1)); else printf("Remainder is zero, x - 1 = %d", (x - 1)); 

PS: Potrebbe anche essere interessante sapere che a volte è disastroso da usare , operatore. Per esempio nella domanda sull'uso di Strtok, il codice non funziona , per errore, OP ha dimenticato di scrivere il nome della funzione e invece di scrivere tokens = strtok(NULL, ",'"); , ha scritto tokens = (NULL, ",'"); e non riceveva errore di compilazione - ma è un'espressione valida che tokens = ",'"; ha causato un ciclo infinito nel suo programma.

L’operatore virgola consente di raggruppare l’espressione laddove ci si aspetta.

Ad esempio può essere utile in alcuni casi:

 // In a loop while ( a--, a < d ) ... 

Ma nel tuo caso non c'è motivo di usarlo. Sarà confuso ... è così ...

Nel tuo caso, è solo per evitare parentesi graffe:

 if(cond) perror("an error occurred"), exit(1); // => if (cond) { perror("an error occurred"); exit(1); } 

Un collegamento a una documentazione operatore virgola .

Nel tuo caso, l’operatore virgola è inutile in quanto potrebbe essere stato utilizzato per evitare parentesi graffe , ma non è il caso poiché lo scrittore le ha già inserite. Pertanto è inutile e potrebbe essere fonte di confusione .

Sembrano esserci pochi usi pratici dell’operatore, ().

Bjarne Stroustrup, The Design and Evolution of C ++

La maggior parte dell’uso frequente della virgola può essere trovata nell’articolo di wikipedia Comma_operator # Usi .

Un uso interessante che ho scoperto quando uso boost :: assign , dove aveva sovraccaricato con giudizio l’operatore per farlo comportarsi come una lista di valori separati da virgole che possono essere spinti alla fine di un object vettoriale

 #include  // for 'operator+=()' using namespace std; using namespace boost::assign; // bring 'operator+=()' into scope { vector values; values += 1,2,3,4,5,6,7,8,9; // insert values at the end of the container } 

Sfortunatamente, l’uso di cui sopra, che era popolare per la prototipazione, ora sembrerebbe arcaico una volta che i compilatori iniziarono a supportare l’ Inizializzazione uniforms

Quindi questo ci lascia di nuovo

Sembrano esserci pochi usi pratici dell’operatore, ().

Bjarne Stroustrup, The Design and Evolution of C ++

Il boost::assign sovraccarica pesantemente l’operatore virgola per ottenere questo tipo di syntax:

 vector v; v += 1,2,3,4,5,6,7,8,9; 

Potrebbe essere utile per l’ operatore dell’itinerario se si desidera eseguire due o più istruzioni quando la condizione è vera o falsa . ma tieni presente che il valore restituito sarà l’ espressione più corretta a causa della regola di valutazione da sinistra a destra dell’operatore virgola (intendo tra parentesi)

Per esempio:

 a