Le istruzioni switch devono sempre contenere una clausola predefinita?


255

In una delle mie prime recensioni di codice (qualche tempo fa), mi è stato detto che è buona norma includere una clausola predefinita in tutte le istruzioni switch. Recentemente ho ricordato questo consiglio ma non ricordo quale fosse la giustificazione. Adesso mi sembra abbastanza strano.

  1. C'è una ragione ragionevole per includere sempre un'istruzione predefinita?

  2. Questa lingua dipende? Non ricordo quale lingua stavo usando in quel momento - forse questo vale per alcune lingue e non per altre?


9
Sarà
ampiamente

Risposte:


277

Cambia caso dovrebbe quasi sempre avere un defaultcaso.

Ragioni per usare a default

1.Per "catturare" un valore imprevisto

switch(type)
{
    case 1:
        //something
    case 2:
        //something else
    default:
        // unknown type! based on the language,
        // there should probably be some error-handling
        // here, maybe an exception
}

2. Gestire azioni "predefinite", in cui i casi riguardano comportamenti speciali.

Lo vedi MOLTO nei programmi basati su menu e negli script di shell bash. Potresti anche vederlo quando una variabile viene dichiarata all'esterno del caso switch ma non inizializzata e ogni caso la inizializza in qualcosa di diverso. Anche in questo caso il valore predefinito deve inizializzarlo in modo che il codice di riga che accede alla variabile non generi un errore.

3. Per mostrare a qualcuno che legge il tuo codice che hai coperto quel caso.

variable = (variable == "value") ? 1 : 2;
switch(variable)
{
    case 1:
        // something
    case 2:
        // something else
    default:
        // will NOT execute because of the line preceding the switch.
}

Questo è stato un esempio troppo semplificato, ma il punto è che qualcuno che legge il codice non dovrebbe chiedersi perché variablenon può essere qualcosa di diverso da 1 o 2.


L'unico caso a cui riesco a pensare di NON usare defaultè quando l'interruttore sta controllando qualcosa in cui è piuttosto ovvio che ogni altra alternativa può essere felicemente ignorata

switch(keystroke)
{
    case 'w':
        // move up
    case 'a':
        // move left
    case 's':
        // move down
    case 'd':
        // move right
    // no default really required here
}

26
Il tuo esempio per quanto riguarda i tasti premuti contraddice il resto di ciò che hai appena detto. : | Penso che un po 'più di spiegazione avrebbe senso come: in questo caso non ti interessa un valore predefinito perché se viene premuto un altro tasto non ti interessa, ma se una variabile passata è diversa dal previsto, ci interessa .
Andrew,

1
Allo stesso tempo, se stai controllando un parametro GET, potresti non voler generare un'eccezione ogni volta che un utente cambia l'URL get in qualcosa che non è valido: [
Andrew

4
@Andrew WASD è comune per i movimenti dei personaggi dei giochi per computer. Non avresti bisogno di azioni predefinite in quanto altri tasti potrebbero essere assegnati ad altre interazioni. In questo tipo di interruttore, è bene chiamare le funzioni di movimento.
jemiloii,

13
Alcuni compilatori avvisano quando si attiva un'enum in caso di mancato rispetto di un caso e l'aggiunta di un caso predefinito eliminerà tale avviso. Dato che questa è la mia esperienza una fonte molto comune di errore (dimenticando di aggiornare un qualche interruttore dopo l'aggiunta di un valore enum), questa sembra una molto buona ragione per omettere un caso di default.
rdb,

3
@virusrocks Se la tua condizione è un enum, puoi comunque avere valori imprevisti. Ad esempio, se si aggiunge un nuovo valore all'enum e si dimentica di aggiornare un parametro. Inoltre, in alcune lingue è possibile assegnare valori interi a enum. In C ++ enum MyEnum { FOO = 0, BAR = 1 }; MyEnum value = (MyEnum)2;crea MyEnumun'istanza valida che non è uguale a FOOo BAR. Se uno di questi problemi causasse un errore nel tuo progetto, un caso predefinito ti aiuterà a trovare l'errore molto rapidamente, spesso senza il debugger necessario!
cdgraham

54

No.

Che cosa succede se non esiste un'azione predefinita, il contesto è importante. E se ti interessa agire solo su alcuni valori?

Prendi l'esempio della lettura dei tasti premuti per un gioco

switch(a)
{
   case 'w':
     // Move Up
     break;
   case 's':
     // Move Down
     break;
   case 'a':
     // Move Left
     break;
   case 'd':
     // Move Right
     break;
}

Aggiunta:

default: // Do nothing

È solo una perdita di tempo e aumenta la complessità del codice senza motivo.


14
Buon esempio. Ma in effetti, l'aggiunta di una clausola predefinita con un semplice // do nothingcommento chiarisce che è "ok" se non tutti i casi sono coperti, al contrario di altre istruzioni switch in cui questo sarebbe "non ok".
The Nail,

8
No. La codifica non riguarda solo la gestione di alcuni casi. Inoltre funge da documento. Scrivendo default: e commentando come // Non fare niente o qualcos'altro, la leggibilità del codice diventa migliore.
JongAm Park,

22
Non sono d'accordo con gli altri commenti. Aggiunta di default // Non fare nulla aggiunge molto poco a meno che non si sappia come funziona il codice. Posso guardare un'istruzione switch senza un valore predefinito e sapere che il valore predefinito è di non fare nulla. L'aggiunta di disordine non lo rende più ovvio.
Robert Noack,

Esattamente. È nello stesso concetto di consegnare tutti i parametri. Gestisci quelli a cui tieni non gli altri.
Jimmy Kane,

2
@jybrd Se non ti fidi che lo sviluppatore precedente abbia omesso intenzionalmente il valore predefinito, perché dovresti fidarti che fossero corretti per inserirlo? Un valore predefinito potrebbe essere svuotato accidentalmente tanto facilmente quanto completamente mancante. Non è necessario un commento per qualcosa di così banale. Questo è disordine di codice. Scrivi invece test di unità a copertura completa per mostrare le tue intenzioni. (Solo la mia opinione)
Robert Noack

45

NON avere il caso predefinito può effettivamente essere utile in alcune situazioni.

Se i tuoi casi switch sono valori enumerati, non avendo un caso predefinito, puoi ricevere un avviso del compilatore se manchi qualche caso. In questo modo, se in futuro vengono aggiunti nuovi valori enum e si dimentica di aggiungere casi per questi valori nello switch, è possibile scoprire il problema al momento della compilazione. Dovresti comunque assicurarti che il codice esegua le azioni appropriate per i valori non gestiti, nel caso in cui un valore non valido sia stato inserito nel tipo enum. Quindi questo potrebbe funzionare meglio per i casi semplici in cui è possibile tornare all'interno del caso enum piuttosto che interrompere.

enum SomeEnum
{
    ENUM_1,
    ENUM_2,
    // More ENUM values may be added in future
};

int foo(SomeEnum value)
{
    switch (value)
    {
    case ENUM_1:
        return 1;
    case ENUM_2:
        return 2;
    }
    // handle invalid values here
    return 0;
 }

Questa è un'osservazione importante! Si applica ancora di più in Java perché non ti consente di lanciare ints su enum.
Lii,

2
Nel tuo esempio potresti lanciare un'eccezione invece di tornare dopo l'istruzione switch. In questo modo potresti avere sia il controllo statico da parte del compilatore che ottenere un chiaro errore se succede qualcosa di inaspettato.
Lii,

1
Sono d'accordo. In Swift per esempio, switch deve essere esaustivo, altrimenti si ottiene un errore del compilatore! Fornire un valore predefinito consente di mettere a tacere questo errore utile. D'altra parte, fornire un valore predefinito se tutti i casi vengono gestiti genera effettivamente un avviso del compilatore (istruzione non raggiungibile).
Andy,

1
Ho appena corretto un bug, che avrebbe causato un avviso dal compilatore se non ci fosse alcun valore predefinito: quindi in generale un Variabili enum delle variabili non dovrebbe avere alcun valore predefinito.
notan,

42

Userei sempre una clausola predefinita, indipendentemente dalla lingua in cui stai lavorando.

Le cose possono e vanno male. I valori non saranno quelli che ti aspetti, e così via.

Non voler includere una clausola predefinita implica che si è certi di conoscere l'insieme dei possibili valori. Se ritieni di conoscere l'insieme dei possibili valori, quindi, se il valore è al di fuori di questo insieme di possibili valori, vorresti esserne informato: è certamente un errore.

Questo è il motivo per cui dovresti sempre usare una clausola predefinita e generare un errore, ad esempio in Java:

switch (myVar) {
   case 1: ......; break;
   case 2: ......; break;
   default: throw new RuntimeException("unreachable");
}

Non c'è motivo di includere più informazioni oltre alla semplice stringa "non raggiungibile"; se ciò accade effettivamente, dovrai comunque esaminare l'origine, i valori delle variabili ecc. e lo stacktrace di eccezione includerà quel numero di riga, quindi non dovrai perdere tempo a scrivere più testo nel messaggio di eccezione.


39
Preferirei throw new RuntimeException("myVar invalid " + myVar);dal momento che ciò potrebbe fornire informazioni sufficienti per capire e correggere il bug senza dover utilizzare un debugger e ispezionare le variabili, il che può essere difficile se si tratta di un problema che si verifica raramente e che è difficile da riprodurre.
Chris Dodd,

1
Cosa succede se non è una condizione di errore che il valore non corrisponda a uno dei casi?
Gabe,

4
Se non si tratta di una condizione di errore, non sarebbe necessario un default:. Ma più spesso, si tralascia un default perché si pensa che myVarnon si possa mai avere un valore diverso da quelli elencati; tuttavia non riesco a contare il numero di volte in cui sono stato sorpreso nella vita reale quando la variabile ha avuto un valore diverso da quello che "potrebbe" avere. In questi casi, sono stato grato per l'eccezione che mi ha fatto vedere subito, piuttosto che causare qualche altro errore in seguito (più difficile da eseguire il debug) o una risposta errata (potrebbe essere trascurato nei test).
Adrian Smith,

2
Sono d'accordo con Adrian. Fallire duramente e fallire presto.
Slappy

Preferisco lanciare un AssertionErrorinvece di RuntimeException, poiché questa situazione è molto simile a un'asserzione che afferma che myVarè uno dei valori gestiti. Anche la possibilità che qualcuno catturi e ingoia l'errore è minore.
Philipp Wendler,

15

Nella mia azienda, scriviamo software per il mercato dell'avionica e della difesa e includiamo sempre un'istruzione predefinita, poiché TUTTI i casi in un'istruzione switch devono essere gestiti in modo esplicito (anche se si tratta solo di un commento che dice "Non fare nulla"). Non possiamo permetterci che il software si comporti male o si blocchi semplicemente su valori imprevisti (o anche ciò che riteniamo impossibile).

Si può discutere che un caso predefinito non è sempre necessario, ma richiedendolo sempre, è facilmente controllabile dai nostri analizzatori di codice.


1
Ho lo stesso requisito in cui lavoro; scriviamo codice per controller µ incorporati che devono superare severi controlli di sicurezza e sono spesso soggetti a EMI. Sarebbe irresponsabile supporre che una variabile enumerata non avrà mai un valore che non sia nell'elenco di enumerazione.
Oosterwal,

12

Un'istruzione "switch" dovrebbe sempre includere una clausola predefinita? No. Di solito dovrebbe includere un valore predefinito.

Includere una clausola predefinita ha senso solo se c'è qualcosa da fare, come affermare una condizione di errore o fornire un comportamento predefinito. Includere uno "solo perché" è la programmazione cargo-cult e non fornisce alcun valore. È l'equivalente "switch" di dire che tutte le istruzioni "if" dovrebbero includere un "else".

Ecco un esempio banale di dove non ha senso:

void PrintSign(int i)
{
    switch (Math.Sign(i))
    {
    case 1:
        Console.Write("positive ");
        break;
    case -1:
        Console.Write("negative ");
        break;
    default: // useless
    }
    Console.Write("integer");
}

Questo è l'equivalente di:

void PrintSign(int i)
{
    int sgn = Math.Sign(i);
    if (sgn == 1)
        Console.Write("positive ");
    else if (sgn == -1)
        Console.Write("negative ");
    else // also useless
    {
    }
    Console.Write("integer");
}

Non sono d'accordo. IMHO, l'unica volta in cui un default non dovrebbe esistere è se non c'è modo, per ora e sempre, che l'insieme di input possa cambiare, e hai coperto tutti i possibili valori. Il caso più semplice che mi viene in mente è un valore booleano di un database, in cui le uniche risposte (fino a quando non cambia SQL!) Sono true, false e NULL. In ogni altro caso, avere un valore predefinito con un'asserzione o un'eccezione di "Dovrei non succedere!" ha un buon senso. Se il tuo codice cambia e hai uno o più nuovi valori, puoi assicurarti di codificarli e i tuoi test esploderanno in caso contrario.
Harper Shelby,

4
@Harper: il tuo esempio rientra nella categoria di "asserire una condizione di errore".
Gabe,

Asserirei che il mio esempio è la norma e che il piccolo numero di casi in cui uno può essere sicuro al 100% che ogni possibile caso sia coperto e che non sia necessario un valore predefinito sono l'eccezione. La tua risposta è formulata in un modo che la fa sembrare (almeno per me) come se il default del non fare accada il più delle volte.
Harper Shelby,

@Harper: OK, ho modificato il testo per indicare che la situazione del non fare nulla è meno comune.
Gabe,

4
Penso che a default:in questi casi codifichi i tuoi presupposti. Ad esempio, è OK quando sgn==0stampare integer(né positivo né negativo) o è un errore? Per me leggere quel codice, è difficile da dire. Suppongo che non vorrai scrivere zeroin quel caso integer, e che il programmatore abbia ipotizzato che sgnpotesse essere solo -1 o +1. Se così fosse, avere il default:consentirebbe al programmatore di rilevare in anticipo l'errore di assunzione e modificare il codice.
Adrian Smith,

7

A mio avviso, la risposta è "default" è facoltativa, dire che uno switch deve sempre contenere un valore predefinito è come dire che ogni "if-elseif" deve contenere un "else". Se c'è una logica da fare di default, allora dovrebbe esserci l'istruzione 'default', ma altrimenti il ​​codice potrebbe continuare l'esecuzione senza fare nulla.


6

Avere una clausola predefinita quando non è realmente necessaria è la programmazione difensiva Questo di solito porta a un codice che è eccessivamente complesso a causa del troppo codice di gestione degli errori. Questo codice di gestione e rilevamento degli errori danneggia la leggibilità del codice, rende più difficile la manutenzione e alla fine porta a più bug di quanti ne risolva.

Quindi credo che se non si dovesse raggiungere il valore predefinito, non è necessario aggiungerlo.

Si noti che "non dovrebbe essere raggiunto" significa che se è stato raggiunto un bug nel software, è necessario testare valori che potrebbero contenere valori indesiderati a causa dell'input dell'utente, ecc.


3
Un altro motivo comune per ottenere casi imprevisti: altre parti del codice vengono modificate, forse anni dopo.
Hendrik Brummermann l'

In effetti, questo è un comportamento molto pericoloso. Almeno, aggiungerei una clausola assert (). Quindi viene facilmente rilevato ogni volta che c'è un bug.
Kurt Pattyn,

5

Direi che dipende dalla lingua, ma in C se si attiva un tipo enum e si gestisce ogni possibile valore, probabilmente è meglio NON includere un caso predefinito. In questo modo, se in seguito aggiungi un tag enum aggiuntivo e dimentichi di aggiungerlo allo switch, un compilatore competente ti avvertirà del caso mancante.


5
"È improbabile che ogni valore possibile" venga gestito con enumerazioni. È possibile eseguire il cast di un numero intero in un enum anche se quel determinato valore non è definito in modo esplicito. E quale compilatore avverte del caso mancante?
TrueWill

1
@TrueWill: Sì, puoi usare cast espliciti per scrivere codice offuscato che è impossibile da capire; per scrivere un codice comprensibile, dovresti evitarlo. gcc -Wall(una specie del minimo comune denominatore di compilatori competenti) fornisce avvisi su enumerazioni non gestite nelle istruzioni switch.
Chris Dodd,

4

Se sai che l'istruzione switch avrà sempre e solo un set definito rigoroso di etichette o valori, fai solo questo per coprire le basi, in questo modo otterrai sempre risultati validi .. Basta mettere il valore predefinito sull'etichetta che programmaticamente / logicamente essere il miglior gestore per altri valori.

switch(ResponseValue)
{
    default:
    case No:
        return false;
    case Yes;
        return true;
}

I due punti dopo sono defaultancora richiesti qui? O facendo questo consente qualche sintassi speciale che ti ha permesso di ometterlo?
Ponkadoodle,

È richiesto il colon, che era un refuso, grazie per averlo portato alla mia attenzione.
deceduto il

3

Almeno non è obbligatorio in Java. Secondo JLS, dice che può essere presente almeno un caso predefinito. Ciò significa che nessun caso predefinito è accettabile. A volte dipende anche dal contesto in cui si sta utilizzando l'istruzione switch. Ad esempio in Java, il seguente blocco switch non richiede il caso predefinito

private static void switch1(String name) {
    switch (name) {
    case "Monday":
        System.out.println("Monday");
        break;
    case "Tuesday":
        System.out.println("Tuesday");
        break;
    }
}

Ma nel seguente metodo che prevede di restituire una stringa, il caso predefinito è utile per evitare errori di compilazione

    private static String switch2(String name) {
    switch (name) {
    case "Monday":
        System.out.println("Monday");
        return name;

    case "Tuesday":
        System.out.println("Tuesday");
        return name;

    default:
        return name;
    }
}

sebbene sia possibile evitare errori di compilazione per il metodo sopra senza avere il caso predefinito semplicemente avendo una dichiarazione di ritorno alla fine, ma fornire il caso predefinito lo rende più leggibile.


3

Alcune linee guida (obsolete) lo dicono, come MISRA C :

Il requisito per una clausola di default finale è la programmazione difensiva. Questa clausola deve intraprendere le azioni appropriate o contenere un commento adeguato sul motivo per cui non viene intrapresa alcuna azione.

Tale consulenza è obsoleta perché non si basa su criteri attualmente pertinenti. L'evidente omissione è ciò che Harlan Kassler ha detto:

Lasciando il caso predefinito, il compilatore può facoltativamente avvisare o fallire quando vede un caso non gestito. Dopo tutto, la verificabilità statica è migliore di qualsiasi controllo dinamico, e quindi non è un degno sacrificio anche quando è necessario il controllo dinamico.

Come ha anche dimostrato Harlan, dopo il passaggio è possibile ricreare l'equivalente funzionale di un caso predefinito. Il che è banale quando ogni caso è un ritorno anticipato.

L'esigenza tipica di un controllo dinamico è la gestione degli input, in senso lato. Se un valore proviene dall'esterno del controllo del programma, non può essere attendibile.

Questo è anche il punto in cui Misra prende il punto di vista della programmazione difensiva estrema, per cui fino a quando un valore non valido è fisicamente rappresentabile, deve essere verificato, indipendentemente dal fatto che il programma sia dimostrabilmente corretto. Ciò ha senso se il software deve essere il più affidabile possibile in presenza di errori hardware. Ma come ha detto Ophir Yoktan, è meglio che la maggior parte dei software non "gestisca" i bug. Quest'ultima pratica è talvolta chiamata programmazione offensiva .


2

Dovresti avere un valore predefinito per rilevare i valori non previsti in arrivo.

Tuttavia, non sono d'accordo con Adrian Smith sul fatto che il tuo messaggio di errore per impostazione predefinita dovrebbe essere qualcosa di totalmente insignificante. Potrebbe esserci un caso non gestito che non hai previsto (che è una specie di punto) che il tuo utente finirà per vedere e un messaggio come "irraggiungibile" è del tutto inutile e non aiuta nessuno in quella situazione.

Caso in questione, quante volte hai avuto un BSOD assolutamente insignificante? O un'eccezione fatale @ 0x352FBB3C32342?


ad esempio, ricorda che non è solo lo sviluppatore che vede sempre messaggi di errore. Viviamo nel mondo reale, le persone commettono errori.
John Hunt,

2

Se il valore dello switch ( switch (variabile )) non è in grado di raggiungere il caso predefinito, il caso predefinito non è affatto necessario. Anche se manteniamo il caso predefinito, non viene affatto eseguito. È un codice morto.


2

È una "convenzione" di codifica opzionale. A seconda dell'uso è se è necessario o meno. Personalmente credo che se non ne hai bisogno non dovrebbe essere lì. Perché includere qualcosa che non verrà utilizzato o raggiunto dall'utente?

Se le possibilità del caso sono limitate (ovvero un valore booleano), la clausola predefinita è ridondante !


2

Se non esiste un caso predefinito in switchun'istruzione, il comportamento può essere imprevedibile se tale caso si presenta in un determinato momento, cosa non prevedibile in fase di sviluppo. È buona norma includere un defaultcaso.

switch ( x ){
  case 0 : { - - - -}
  case 1 : { - - - -}
}

/* What happens if case 2 arises and there is a pointer
* initialization to be made in the cases . In such a case ,
* we can end up with a NULL dereference */

Tale pratica può comportare un bug simile dereferenza NULL , la perdita di memoria e altri tipi di bug gravi .

Ad esempio supponiamo che ogni condizione inizializzi un puntatore. Ma sedefault si suppone che si verifichi un caso e se in questo caso non inizializziamo, allora c'è la possibilità di approdare con un'eccezione puntatore nulla. Quindi si suggerisce di usare defaultun'istruzione case, anche se può essere banale.


2

Il caso predefinito potrebbe non essere necessario nello switch utilizzato da enum. quando switch conteneva tutto il valore, il caso predefinito non verrà mai eseguito. Quindi, in questo caso, non è necessario.


1

Dipende da come funziona lo switch in una determinata lingua, tuttavia nella maggior parte delle lingue quando non viene trovato nessun caso, l'esecuzione passa attraverso l'istruzione switch senza preavviso. Immagina di aspettarti un insieme di valori e di gestirli in switch, tuttavia ottieni un altro valore nell'input. Non succede nulla e non sai che non è successo niente. Se hai scoperto il caso per impostazione predefinita, sapresti che c'era qualcosa di sbagliato.


1

Non sono d'accordo con la risposta più votata di Vanwaril sopra.

Qualsiasi codice aggiunge complessità. Anche i test e la documentazione devono essere fatti per questo. Quindi è sempre bene se puoi programmare usando meno codice. La mia opinione è che uso una clausola predefinita per le istruzioni switch non esaustive mentre non uso una clausola predefinita per le istruzioni switch complete. Per essere sicuro di aver fatto bene, utilizzo uno strumento di analisi del codice statico. Quindi andiamo nei dettagli:

  1. Dichiarazioni switch non esaustive: devono sempre avere un valore predefinito. Come suggerisce il nome, si tratta di affermazioni che non coprono tutti i possibili valori. Anche questo potrebbe non essere possibile, ad esempio un'istruzione switch su un valore intero o su una stringa. Qui vorrei usare l'esempio di Vanwaril (Va detto che penso che abbia usato questo esempio per dare un suggerimento sbagliato. Lo uso qui per indicare il contrario -> Usa un'istruzione predefinita):

    switch(keystroke)
    {
      case 'w':
        // move up
      case 'a':
        // move left
      case 's':
        // move down
      case 'd':
        // move right
      default:          
        // cover all other values of the non-exhaustive switch statement
    }
    

    Il giocatore può premere qualsiasi altro tasto. Quindi non potremmo fare nulla (questo può essere mostrato nel codice semplicemente aggiungendo un commento al caso predefinito) o dovrebbe ad esempio stampare qualcosa sullo schermo. Questo caso è rilevante in quanto potrebbe accadere.

  2. Istruzioni switch esaustive: quelle istruzioni switch coprono tutti i possibili valori, ad esempio un'istruzione switch su un elenco di tipi di sistema di classificazione. Quando si sviluppa il codice per la prima volta, è facile coprire tutti i valori. Tuttavia, poiché siamo umani, c'è una piccola possibilità di dimenticarsene. Inoltre, se in seguito aggiungi un valore enum tale che tutte le istruzioni switch devono essere adattate per renderle esaustive, si apre il percorso all'inferno di errore. La soluzione semplice è uno strumento di analisi del codice statico. Lo strumento dovrebbe controllare tutte le istruzioni switch e verificare se sono esaustive o se hanno un valore predefinito. Ecco un esempio per un'istruzione switch completa. Per prima cosa abbiamo bisogno di un enum:

    public enum GradeSystemType {System1To6, SystemAToD, System0To100}
    

    Quindi abbiamo bisogno di una variabile come questa enum GradeSystemType type = .... Un'esaustiva istruzione switch sarebbe quindi simile a questa:

    switch(type)
    {
      case GradeSystemType.System1To6:
        // do something
      case GradeSystemType.SystemAToD:
        // do something
      case GradeSystemType.System0To100:
        // do something
    }
    

    Quindi, se estendiamo, GradeSystemTypead esempio, System1To3lo strumento di analisi del codice statico dovrebbe rilevare che non esiste una clausola predefinita e che l'istruzione switch non è esaustiva, quindi siamo salvati.

Solo un'altra cosa. Se utilizziamo sempre una defaultclausola, può accadere che lo strumento di analisi del codice statico non sia in grado di rilevare istruzioni di commutazione esaustive o non esaustive poiché rileva sempre la defaultclausola. Questo è super negativo poiché non saremo informati se estendiamo l'enum di un altro valore e dimentichiamo di aggiungerlo a un'istruzione switch.


0

Le istruzioni switch devono sempre contenere una clausola predefinita? Non possono esistere casi di switch senza il caso predefinito, nel caso di caso il caso predefinito attiverà il valore di commutazione switch(x)in questo caso x quando non corrisponde ad altri valori di caso.


0

Credo che questo sia abbastanza specifico per il linguaggio e per il caso C ++ un punto minore per la classe enum tipo di . Che sembra più sicuro del C enum tradizionale. MA

Se guardi l'implementazione di std :: byte è qualcosa di simile a:

enum class byte : unsigned char {} ;

Fonte: https://en.cppreference.com/w/cpp/language/enum

E considera anche questo:

Altrimenti, se T è un tipo di enumerazione con ambito o senza ambito con tipo sottostante fisso, e se l'elenco di parentesi graffe ha un solo inizializzatore e se la conversione dall'inizializzatore al tipo sottostante non è restrittiva, e se l'inizializzazione è l'inizializzazione dell'elenco diretto, quindi l'enumerazione viene inizializzata con il risultato della conversione dell'inizializzatore nel tipo sottostante.

(dal C ++ 17)

Fonte: https://en.cppreference.com/w/cpp/language/list_initialization

Questo è un esempio di enum class che rappresenta valori che non sono definiti enumeratori. Per questo motivo non puoi fidarti completamente di enumerazioni. A seconda dell'applicazione potrebbe essere importante.

Tuttavia, mi piace molto quello che ha detto @Harlan Kassler nel suo post e inizierò a utilizzare quella strategia in alcune situazioni.

Solo un esempio di classe enum non sicura:

enum class Numbers : unsigned
{
    One = 1u,
    Two = 2u
};

int main()
{
    Numbers zero{ 0u };
    return 0;
}
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.