2009-02-07 8 views
7

ci sono problemi di comprensione/manutenibilità che derivano da codice comeSi tratta di un uso ragionevole dell'operatore ternario?

inVar1 == 0 ? NULL : v.push_back(inVar1); 
inVar2 == 0 ? NULL : v.push_back(inVar2); 

e così via.

L'idea forse confusa è l'utilizzo dell'operatore ternario per il flusso del programma piuttosto che l'assegnazione di variabili, che è la spiegazione normale.

Non ho visto gli standard di codifica sul lavoro che affrontano questo utilizzo, quindi mentre mi sento a mio agio nel farlo vorrei sapere se c'è una buona ragione per non farlo.

+1

e nota che la tua cosa è anche sbagliata in C++. se un operando è vuoto, allora l'altro deve essere troppo, o deve lanciare: inVar1 == 0? (void) NULL: v.push_back (inVar1); basta usare un semplice se :) –

+0

Non sono d'accordo con "sbagliato" - VisualStudio 2003 e 2005 lo consentono. Forse possiamo discutere su cosa "sbagliato" significa ...? – user63572

+0

significa sbagliato quando il tuo codice non è valido C++, ovviamente. un ramo ha tipo zero e un altro ha tipo int, long o così. non è valido vuoto e int bite. forse vC++ ha un'estensione che permette questo - non lo so. –

risposta

35

Penso che sia confuso e molto più difficile da leggere rispetto alla semplice digitazione;

if (inVar != 0) 
    v.push_back(inVar); 

ho dovuto eseguire la scansione ad esempio più volte per capire quale sia il risultato sarebbe con certezza. Preferirei persino una dichiarazione if() {} di una sola riga del tuo esempio - e odio le affermazioni su riga singola :)

+0

Penso che ed-malabar dovrebbe giudicare la leggibilità determinando quanto tempo è stato _spent_ tentare di capire il codice. Semanticamente, è semplicemente semplice. Non dovrebbe portarmi 30 secondi buoni per capirlo appieno. – Calyth

+1

Sono d'accordo - sembra che il consenso popolare sia che si impiega più tempo ad analizzare il codice piuttosto che scorrere. – user63572

10

Penso che questo dovrebbe essere evitato. È possibile utilizzare una dichiarazione di 1 riga se al suo posto.

if(inVar1 != 0) v.push_back(inVar1); 
+0

Questo è brutto - non c'è mai una buona ragione per non contrassegnare i blocchi con bracec circostanti. –

+3

@mP: c'è: pigrizia e talvolta anche leggibilità; Faccio spesso cose come 'if (! Buffer) restituisce NULL;' per verificare i valori restituiti/condizioni di errore, e trovo molto più facile leggere if * not * circondato da parentesi, indipendentemente da ciò che alcune convenzioni di codifica potrebbero dire .. – Christoph

+5

@mP: non devi seguire ciecamente le convenzioni di codifica senza * pensare * al perché esistono. Non hai sempre bisogno di parentesi graffe attorno a ogni se blocco. –

25

L'operatore ternario è pensato per restituire un valore.

IMO, non deve mutare lo stato e deve essere utilizzato il valore restituito.

Nell'altro caso, utilizzare se le istruzioni. Se le istruzioni sono pensate per eseguire blocchi di codice.

+0

Ma il valore di ritorno è in uso. – abelito

1

Penso che sarebbe meglio servire nel fare una struttura adeguata se. Preferisco persino avere sempre parentesi con le mie strutture if, nel caso in cui devo aggiungere righe dopo l'esecuzione condizionale.

if (inVar != 0) { 
    v.push_back(inVar); 
} 
6

I compilatori in questi giorni eseguiranno un se veloce come un operatore ternario.

L'obiettivo dovrebbe essere la facilità con cui un altro sviluppatore di software può leggere.

Io voto per

if (inVar != 0) 
{ 
    v.push_back(inVar); 
} 

il motivo per cui le staffe ... perché un giorno si consiglia di mettere qualcos'altro in là e le staffe sono pre-fatto per voi. La maggior parte degli editori in questi giorni li inserirà comunque.

+1

Tranne in rari casi, odio l'operatore ternario. È difficile da leggere, difficile da esaminare visivamente per i campi discreti coinvolti e facile da perdere all'interno di un più ampio blocco di codice. – Joe

+1

Sono d'accordo, lo evito come la peste. –

+1

Ho sempre pensato che il ragionamento è strano (mettere subito le parentesi) - È davvero più difficile metterli in un secondo momento quando decidi di averne bisogno? – Eclipse

2

Mentre, in pratica, sono d'accordo con i sentimenti di coloro che scoraggiano questo tipo di scrittura (durante la lettura, si deve fare lavoro extra per eseguire la scansione l'espressione per i suoi effetti collaterali), mi piacerebbe offrire

!inVar1 ?: v.push_back(inVar1); 
!inVar2 ?: v.push_back(inVar2); 

... se stai andando oscuro, quello è. GCC consente x ?: y in sostituzione di x ? x : y. :-)

+0

Bello! La mia intenzione non era quella di offuscare il codice, semplicemente per evitare un'inevitabile espansione verticale. VS2005 non piace?: Però. Dang. – user63572

+0

Non ho detto che fosse una buona idea, l'ho semplicemente offerta come una possibilità. – ephemient

6

L'utilizzo del l'operatore ternario si guadagna nulla e si fa male i codici leggibilità.

Poiché l'operatore ternario restituisce un valore che non si sta utilizzando è un codice dispari. L'uso di un numero if è molto più chiaro in un caso come il tuo.

11

Il ternario è una buona cosa e generalmente ne promuovo l'utilizzo.

Quello che stai facendo qui appanna però la sua credibilità. È più breve, sì, ma inutilmente complicato.

5

Come indicato nei commenti, questo isn't valid C++. GCC, per esempio, emetterà un errore su questo codice:

error: `(&v)->std::vector<_Tp, _Alloc>::push_back [with _Tp = int, _Alloc = 
std::allocator<int>](((const int&)((const int*)(&inVar1))))' has type `void' 
and is not a throw-expression 

Tuttavia, che può essere lavorato in giro per colata:

inVar1 == 0 ? (void)0 : v.push_back(inVar1); 
inVar2 == 0 ? (void)0 : v.push_back(inVar2); 

Ma a quale costo? E per quale scopo?

Non è come con l'operatore ternario qui è più conciso che un se-statement in questa situazione:

inVar1 == 0 ? NULL : v.push_back(inVar1); 
if(inVar1 != 0) v.push_back(inVar1); 
2

Io uso operatore ternario quando ho bisogno di chiamare una funzione con argomenti condizionali - in questo caso è meglio quindi if.

Confronta:

printf("%s while executing SQL: %s", 
     is_sql_err() ? "Error" : "Warning", sql_msg()); 

con

if (is_sql_err()) 
    printf("Error while executing SQL: %s", sql_msg()); 
else 
    printf("Warning while executing SQL: %s", sql_msg()); 

trovo il primo è più attraente. Ed è conforme al principio DRY, a differenza di quest'ultimo - non è necessario scrivere due linee quasi identiche.

+0

La versione dell'operatore condizionale ternario enfatizza anche la parte principale del codice - la stampa - piuttosto che la parte minore del codice - se si tratta di un avviso o di un errore. –

0

Se si dispone di più chiamate di metodi in uno o entrambi gli argomenti del tenario, è sbagliato. Tutte le linee di codice indipendentemente da quale affermazione dovrebbe essere breve e semplice, idealmente non aggravata.

0

Un'istruzione if corretta è più leggibile, come altri hanno menzionato. Inoltre, quando si sta passando attraverso il codice con un debugger, non sarà in grado di vedere facilmente quale ramo di un se è preso quando tutto è in una linea o stai usando un espressione ternaria:

if (cond) doIt(); 

cond ? noop() : doIt(); 

considerando che la seguente è molto più bello per passare (se si dispone le parentesi o meno):

if (cond) { 
    doIt(); 
} 
1

credo che a volte il ternario sono un male necessario in liste di inizializzazione per i costruttori. Li uso principalmente per i costruttori in cui voglio allocare memoria e impostare un puntatore in modo che punti prima del corpo del costruttore.

Un esempio, si supponga di avere una classe di archiviazione numero intero che si voleva avere un vettore prendere come input, ma la rappresentazione interna è un array:

class foo 
{ 
public: 
    foo(std::vector<int> input); 
private: 
    int* array; 
    unsigned int size; 
}; 

foo:foo(std::vector<int> input):size(input.size()), array((input.size()==0)? 
     NULL : new int[input.size]) 
{ 
    //code to copy elements and do other start up goes here 
} 

Questo è come io uso l'operatore ternario. Non penso che sia così confuso come alcune persone, ma penso che si dovrebbe limitare quanto lo usano.

1

La maggior parte dei ternari torturati (come è per l'allitterazione?) Vedo che si tratta semplicemente di tentativi di mettere la logica che appartiene veramente a un'istruzione if in un punto in cui un'istruzione if non appartiene o non può andare.

Per esempio:

if (inVar1 != 0) 
    v.push_back(inVar1); 
if (inVar2 != 0) 
    v.push_back(inVar2); 

opere partendo dal presupposto che v.push_back è vuoto, ma che cosa se si tratta di restituire un valore che ha bisogno di avere passato a un'altra funzione? In quel caso, avrebbe dovuto essere simile a questa:

SomeType st; 
if (inVar1 != 0) 
    st = v.push_back(inVar1); 
else if (inVar2 != 0) 
    st = v.push_back(inVar2); 
SomeFunc(st); 

Ma che c'è di più da digerire per un semplice pezzo di codice tale. La mia soluzione: definire un'altra funzione.

SomeType GetST(V v, int inVar1, int inVar2){ 
    if (inVar1 != 0) 
     return v.push_back(inVar1); 
    if (inVar2 != 0) 
     return v.push_back(inVar2);   
} 

//elsewhere 
SomeFunc(GetST(V v, inVar1, inVar2)); 

Ad ogni modo, il punto è questo: se si dispone di una logica che è troppo torturato per un ternario ma sarà ingombrare il codice se è messo in un'istruzione if, metterlo da qualche altra parte!

0

Come accennato, non è più breve o più chiaro di un'istruzione di 1 riga se. Tuttavia, non è nemmeno più - e non è poi così difficile da digerire. Se conosci l'operatore ternario, è abbastanza ovvio cosa sta succedendo.

Dopo tutto, io non credo che nessuno avrebbe avuto un problema se veniva assegnato ad una variabile (anche se era stato mutando così):

var2 = inVar1 == 0 ? NULL : v.push_back(inVar1); 

Il fatto che l'operatore ternario sempre restituisce un valore - IMO - è irrilevante. Non c'è certamente nessun requisito per l'utilizzo di tutti i valori di ritorno ... dopotutto, un'assegnazione restituisce un valore.

Detto questo, lo sostituisco con un'istruzione if se l'ho trovato attraverso un ramo NULL.

Ma, se ha sostituito una linea 3 if:

if (inVar == 0) { 
    v.doThingOne(1); 
} else { 
    v.doThingTwo(2); 
} 

con:

invar1 == 0 ? v.doThingOne(1) : v.doThingTwo(2); 

ho potrebbe lasciare ... a seconda del mio umore. ;)

1
inVar1 != 0 || v.push_back(inVar1); 
inVar2 != 0 || v.push_back(inVar2); 

schema comune trovato in lingue come Perl.

+1

Questa è la rielaborazione inizialmente più confusa del mio codice di esempio in questa discussione. Ma dopo il WTF iniziale, analizza. E mi fa immaginare che gli sviluppatori di Perl abbiano monitor davvero minuscoli. – user63572

+0

Non è un modello comune in lingue come C, però. –