Ovviamente:
Whatever(Arguments)
{
if(!FileExists(file))
goto notexists;
contents = OpenFile(file); // <-- prevents inclusion in if
if(!SomeTest(contents))
goto notexists;
DoSomething(contents);
return;
notexists:
DefaultAction();
}
Hai detto di essere aperto anche a soluzioni malvagie, quindi usare il conteggio del goto del male, no?
In effetti, a seconda del contesto, questa soluzione potrebbe essere meno malvagia di entrambi i malvagi che eseguono l'azione due volte o una variabile extra malvagia. L'ho avvolto in una funzione, perché non sarebbe sicuramente OK nel mezzo di una lunga funzione (non ultimo a causa del ritorno nel mezzo). Ma la funzione lunga non è OK, punto.
Quando hai delle eccezioni, saranno più facili da leggere, specialmente se puoi avere OpenFile e DoSomething semplicemente lanciare un'eccezione se le condizioni non sono soddisfatte, quindi non hai bisogno di controlli espliciti. D'altra parte in C ++, Java e C # lanciare un'eccezione è un'operazione lenta, quindi dal punto di performance, il goto è ancora preferibile.
Nota su "male": Domande frequenti su C ++ 6.15 definisce " male "come:
It means such and such is something you should avoid most of the time, but not something you should avoid all the time. For example, you will end up using these "evil" things whenever they are "the least evil of the evil alternatives."
E questo vale per goto
in questo contesto. I costrutti di controllo del flusso strutturato sono migliori il più delle volte, ma quando entri nella situazione in cui accumulano troppi mali, come il compito in condizioni, annidando più di 3 livelli in profondità, duplicando codice o condizioni lunghe, goto
potrebbe semplicemente finire per essere meno cattivo.