2009-10-16 3 views
10

Il nostro strumento di analisi statica si lamenta "un qualificatore di tipo inutile su tipo di ritorno" quando abbiamo prototipi in file di intestazione, quali:Si dovrebbero usare qualificatori di tipo inutili sui tipi di ritorno, per chiarezza?

const int foo(); 

Abbiamo definito in questo modo perché la funzione restituisce una costante che non cambierà mai , pensando che l'API sembrava più chiara con const sul posto.

Mi sembra che questo sia simile all'inizializzazione esplicita delle variabili globali a zero per chiarezza, anche se lo standard C già afferma che tutti i globali saranno inizializzati a zero se non inizializzati esplicitamente. Alla fine della giornata, non importa. (Ma lo strumento di analisi statica non si lamenta di quello.)

La mia domanda è, c'è qualche motivo che questo potrebbe causare un problema? Dovremmo ignorare gli errori generati dallo strumento o dovremmo placare lo strumento al costo possibile di un'API meno chiara e coerente? (Restituisce altre costanti const char* con cui lo strumento non ha problemi.)

risposta

25

Di solito è meglio che il tuo codice descriva il più accuratamente possibile quello che sta succedendo. Stai ricevendo questo avviso perché loin const int foo(); non ha praticamente alcun significato. L'API sembra solo più chiara se non sai cosa significa la parola chiave const. Non sovraccaricare il significato in questo modo; static è già abbastanza grave e non c'è motivo di aggiungere ulteriori possibilità di confusione.

const char * significa qualcosa di diverso da const int, motivo per cui il tuo strumento non si lamenta. Il primo è un puntatore a una stringa costante, nel senso che qualsiasi codice che richiami la funzione che restituisce quel tipo non dovrebbe provare a modificare il contenuto della stringa (potrebbe essere in ROM, ad esempio). In quest'ultimo caso, il sistema non ha modo di imporre di non apportare modifiche allo int restituito, quindi il qualificatore non ha senso. Una più stretta parallelamente ai tipi di ritorno sarebbe:

const int foo(); 
char * const foo2(); 

che sia sì che il vostro analisi statica per dare l'avvertimento - l'aggiunta di un qualificatore const ad un valore di ritorno è un'operazione senza senso. Ha senso solo quando si ha un parametro di riferimento (o tipo di ritorno), come nell'esempio const char *.

In realtà, ho solo fatto un piccolo programma di test, e GCC mette in guardia anche esplicitamente su questo problema:

test.c:6: warning: type qualifiers ignored on function return type 

quindi non è solo il vostro programma di analisi statica che si lamenta.

+3

concordato. La mia reazione istantanea a un tipo di ritorno const è di presupporre che il ritorno sia un riferimento/puntatore a un buffer condiviso che non dovrebbe essere modificato. Il mio modello mentale è che const si applica al contenitore (ad es. Variabile), non al contenuto. Ad esempio, in "const char *" il const si applica alla stringa puntata, mentre se hai "const int i = 5;", puoi ancora scrivere "i + 1" in un'espressione - puoi lavorare con valore finché non provi a cambiare la variabile. Con un semplice ritorno int, non hai un contenitore, solo un valore. – Steve314

+0

Immagino che le persone vengano confuse a causa del comportamento strano della parola chiave 'const'. Voglio dire che 'const char * c' e' char const * c' sono la stessa cosa. Se la precedente sintassi non fosse legale, le cose sarebbero molto meno confuse. Avremmo solo 'char const * c' e' char * const c' e tutti saprebbero cosa sta succedendo. Forse ci sto pensando troppo. –

+0

Ok, sono convinto. Grazie per aver segnalato l'avviso GCC. Vorrei che sarebbe stato un errore piuttosto che un avvertimento, quindi aree grigie come questa non si presentano. – mpontillo

4

È possibile utilizzare una tecnica diversa per illustrare l'intento senza rendere gli strumenti infelici.

#define CONST_RETURN 

CONST_RETURN int foo(); 

Non hai un problema con const char * perché è dichiarare un puntatore a caratteri costanti, non un puntatore costante.

+3

Ma dato che non c'è il controllo del compilatore di quella richiesta CONST_RETURN, non è meglio avere un commento? In questo modo, almeno non * sembra * qualcosa che il compilatore deve aver controllato. – Steve314

+0

Sì, un commento potrebbe essere ancora migliore. –

+0

Ci sono alcune API/librerie che ho visto che hanno fatto qualcosa di simile a questo. Se hai una funzione che restituisce sempre lo stesso numero, '#define foo() VALUE' potrebbe essere il trucco. –

3

const int foo() è molto diverso da const char* foo(). const char* foo() restituisce un array (di solito una stringa) il cui contenuto non può essere modificato.Pensate alla differenza tra:

const char* a = "Hello World"; 

e

const int b = 1; 

a è ancora una variabile e possono essere assegnati ad altre stringhe che non possono cambiare mentre b non è una variabile. Così

const char* foo(); 
const char* a = "Hello World\n"; 
a = foo(); 

è consentito ma

const int bar(); 
const int b = 0; 
b = bar(); 

non è consentito, anche con la dichiarazione di constbar().

+2

'const int b = bar()' è permesso, perché puoi sempre inizializzare una variabile 'const'. Vuoi dire 'const int b; b = bar() '? –

+0

Hai ragione. Risposta corretta – atlpeg

4

Ignorare const per il momento, foo() restituisce un valore. Si può fare

int x = foo(); 

e assegnare il valore restituito dal foo() alla variabile x, più o meno allo stesso modo si può fare

int x = 42; 

per assegnare il valore 42 alla variabile x.
Ma non è possibile modificare 42 ... o il valore restituito da foo(). Dicendo che il valore restituito da foo() non può essere modificato, applicando la parola chiave const al tipo di foo() non si ottiene nulla.

Valori non può essere const (o restrict, o volatile). Solo gli oggetti possono avere qualificatori di tipo.


contrasto con

const char *foo(); 

In questo caso, foo() restituisce un puntatore ad un oggetto. L'oggetto indicato dal valore restituito può essere qualificato const.

3

L'interno viene restituito da copia. Può essere una copia di un const, ma quando è assegnato a qualcos'altro, quel qualcosa in virtù del fatto che è assegnabile, non può essere per definizione un const.

La parola chiave const ha una semantica specifica all'interno della lingua, mentre qui si sta abusando di esso come essenzialmente un commento. Piuttosto che aggiungere chiarezza, suggerisce piuttosto un fraintendimento della semantica del linguaggio.

+0

Cosa fa const int PI = 3.14159; int myvalue = 2 * PI; fare?Stai dicendo che PI non dovrebbe essere dichiarato const? Se il valore restituito dalla funzione non cambierà, è una costante, quindi è abbastanza ragionevole dichiararlo come tale. –

+0

@Jason: abbastanza ragionevole ma anche abbastanza ridondante, motivo per cui gli strumenti trattano questo come un avviso e non un errore. Nell'esempio il 'const' è * non * ridondante. –

+2

Non è la stessa cosa. Se ho una funzione 'getPi()' che ha semplicemente 'return PI' in essa, il valore restituito non è' const'. Non * può * essere 'const' a causa delle convenzioni del linguaggio pass-by-value (e return-by-value). –

2

Sì. Consiglierei di scrivere il codice "esplicitamente", perché rende chiaro a chiunque (incluso te stesso) quando legge il codice cosa intendevi. Stai scrivendo il codice per altri programmatori per leggere, non per soddisfare i capricci del compilatore e gli strumenti di analisi statici!

(Tuttavia, è necessario fare attenzione che qualsiasi "codice non necessario" non generi codice diverso da generare!)

Alcuni esempi di codifica esplicita migliorando la leggibilità/manutenibilità:

  • ho posto parentesi intorno porzioni di espressioni aritmetiche per specificare in modo esplicito quello che voglio che accada. Ciò rende chiaro a tutti i lettori cosa intendevo e mi fa risparmiare problemi di precedenza:

     
    int a = b + c * d/e + f;  // Hard to read- need to know precedence 
    int a = b + ((c * d)/e) + f; // Easy to read- clear explicit calculations 
    

  • In C++, se si esegue l'override di una funzione virtuale, quindi nella classe derivata si può dichiararlo senza menzionare "virtuale". Chiunque legga il codice non può dire che è una funzione virtuale, che può essere disastrosamente fuorviante! Tuttavia puoi tranquillamente usare la parola chiave virtuale:

    virtual int MyFunc()
    e questo rende chiaro a chiunque legga l'intestazione della tua classe che questo metodo sia virtuale. (Questo "errore di sintassi C++" è risolto in C# richiedendo l'uso della parola chiave "override" in questo caso - più prova se qualcuno ne avesse bisogno che perdere il "virtuale non necessario" è una pessima idea)

Questi sono entrambi esempi chiari in cui l'aggiunta di codice "non necessario" renderà il codice più leggibile e meno soggetto a bug.

+2

Non stai parlando della stessa cosa. Come dice @pmg, ** valori ** non possono essere 'const 'o qualsiasi altra cosa. Solo gli oggetti possono. Dire che è prezioso attaccare un'etichetta 'const' su qualcosa che per definizione non' const' è chiaramente una cattiva idea. Confonde la semantica inutilmente. –