Operatore Condizionale Ternario in C++

Concetti Chiave
  • L'operatore condizionale in C++ consente di eseguire una semplice logica if-else all'interno di un'espressione.
  • La sintassi dell'operatore condizionale è condizione ? espressione1 : espressione2.
  • La condizione viene valutata per determinare quale delle due espressioni eseguire.
  • Solo una delle due espressioni viene valutata, a seconda del risultato della condizione.
  • L'operatore condizionale ha una precedenza bassa, quindi spesso è necessario usare le parentesi.
  • L'operatore condizionale può essere annidato, ma l'annidamento eccessivo può ridurre la leggibilità del codice.

Operatore Condizionale

L'operatore condizionale (l'operatore ?:) ci permette di incorporare una semplice logica if-else all'interno di un'espressione. L'operatore condizionale ha la seguente forma:

condizione ? espressione1 : espressione2;

dove condizione è un'espressione che viene usata come condizione ed espressione1 ed espressione2 sono espressioni dello stesso tipo (o tipi che possono essere convertiti in un tipo comune). Questo operatore viene eseguito valutando condizione. Se la condizione è vera, allora espressione1 viene valutata; altrimenti, espressione2 viene valutata. Come esempio, possiamo usare un operatore condizionale per determinare se un voto è sufficiente o insufficiente:

string voto_finale = (voto < 60) ? "bocciato" : "promosso";

La condizione controlla se voto è minore di 60. Se sì, il risultato dell'espressione è "bocciato"; altrimenti il risultato è "promosso". Come gli operatori AND logico e OR logico (&& e ||), l'operatore condizionale garantisce che solo una tra espressione1 o espressione2 venga valutata.

Il risultato dell'operatore condizionale è un l-value se entrambe le espressioni sono l-value o se si convertono in un tipo l-value comune. Altrimenti il risultato è un r-value.

Annidamento di Operazioni Condizionali

Possiamo annidare un operatore condizionale dentro un altro. Cioè, l'operatore condizionale può essere usato come condizione o come una o entrambe le espressioni di un'altra espressione condizionale. Come esempio, useremo una coppia di condizionali annidati per eseguire un test a tre vie per indicare se un voto è ottimo, una sufficienza ordinaria o un'insufficienza:

voto_finale = (voto > 90) ? "ottimo"
                          : (voto < 60) ? "bocciato" : "promosso";

La prima condizione controlla se il voto è superiore a 90. Se sì, l'espressione dopo il ? viene valutata, che produce "ottimo". Se la condizione fallisce, il ramo : viene eseguito, che è esso stesso un'altra espressione condizionale. Questo condizionale chiede se il voto è inferiore a 60. Se sì, il ramo ? viene valutato e produce "bocciato". Se no, il ramo : restituisce "promosso".

L'operatore condizionale è associativo a destra, il che significa (come al solito) che gli operandi si raggruppano da destra a sinistra. L'associatività spiega il fatto che il condizionale destro, ossia quello che confronta voto con 60, forma il ramo : dell'espressione condizionale sinistra.

I condizionali annidati diventano rapidamente illeggibili. È buona prassi annidare non più di due o tre.

Uso di un Operatore Condizionale in un'Espressione di Output

L'operatore condizionale ha una precedenza abbastanza bassa. Quando incorporiamo un'espressione condizionale in un'espressione più grande, di solito dobbiamo mettere tra parentesi la sotto-espressione condizionale. Ad esempio, usiamo spesso l'operatore condizionale per stampare un valore o un altro, a seconda del risultato di una condizione. Un operatore condizionale incompletamente messo tra parentesi in un'espressione di output può avere risultati sorprendenti:

// stampa promosso o bocciato
cout << ((voto < 60) ? "bocciato" : "promosso");

// stampa 1 o 0!
cout << (voto < 60) ? "bocciato" : "promosso"; // stampa 1 o 0!

// ERRORE: confronta cout con 60
cout << voto < 60 ? "bocciato" : "promosso";

La seconda espressione usa il confronto tra voto e 60 come operando dell'operatore <<. Il valore 1 o 0 viene stampato, a seconda che voto < 60 sia vero o falso. L'operatore << restituisce cout, che viene testato come condizione per l'operatore condizionale. Cioè, la seconda espressione è equivalente a

cout << (voto < 60); // stampa 1 o 0

// testa cout e poi produce uno dei due letterali
// a seconda che cout sia vero o falso
cout ? "bocciato" : "promosso";

L'ultima espressione è un errore perché è equivalente a

// minore di ha precedenza inferiore allo shift,
// quindi stampa prima voto
cout << voto;

// poi confronta cout con 60!
cout < 60 ? "bocciato" : "promosso";

Esercizi

  • Scrivi un programma per usare un operatore condizionale per trovare gli elementi in un vector<int> che hanno valore dispari e raddoppiare il valore di ciascuno di tali elementi.

    Soluzione:

    #include <iostream>
    #include <vector>
    
    int main() {
        std::vector<int> v = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    
        // Scorre il vettore e raddoppia solo gli elementi dispari
        for (auto &elemento : v)
            elemento = ((elemento % 2 == 0) ? elemento : elemento * 2);
    
        // Stampa il vettore
        for (auto &elemento : v)
            std::cout << elemento << std::endl;
    
        return 0;
    }
    
  • La seguente espressione non riesce a compilare a causa della precedenza degli operatori. Usando la tabella degli operatori, spiega perché fallisce. Come la correggeresti?

    string s = "word";
    string pl = s + s[s.size() - 1] == 's' ? "" : "s" ;
    

    Soluzione:

    Per risolvere questo problema dobbiamo andare per ordine e scomporre l'espressione.

    Per prima cosa, viene valutato il membro s.size() a cui viene sottratto 1 e poi viene valutato l'indice. Quindi viene valutata la somma, +, e poi l'uguaglianza. Solo alla fine viene valutato l'operatore ternario. Quindi possiamo scrivere il tutto come:

    string s = "word";
    
    auto i = s.size() - 1;
    auto c = s[i];
    
    string s1 = s + c;
    
    // ERRORE: Confronta una stringa con un carattere
    bool r = (s1 == 's');
    
    string pl = r1 ? "" : "s";
    

    Quindi, per correggere l'espressione basta scrivere "s" al posto di 's':

    string s = "word";
    string pl = s + s[s.size() - 1] == "s" ? "" : "s" ;
    
  • L'operatore ternario condizionale è associativo a destra. Riprendiamo l'espressione vista sopra:

    voto_finale = (voto > 90) ? "ottimo"
                            : (voto < 60) ? "bocciato" : "promosso";
    

    Cosa accadrebbe se l'operatore fosse associativo a sinistra? Scrivi l'equivalente espressione con parentesi per l'associatività a sinistra e spiega il comportamento dell'espressione.

    Soluzione:

    Se l'operatore fosse associativo a sinistra, l'espressione verrebbe interpretata come:

    voto_finale = ((voto > 90) ? "ottimo" : (voto < 60)) ?
                                            "bocciato" : "promosso";
    

    In altre parole, l'espressione non compilerebbe. Questo perché verrebbe valutata la prima condizione voto > 90. Se vera, l'espressione più interna varrebbe "ottimo", ossia una stringa. Se falsa varrebbe voto < 60, ossia un booleano. Ma questi sono tipi diversi per cui avremmo un errore di compilazione.