Perché lanciare valori di ritorno inutilizzati su void?


112
int fn();

void whatever()
{
    (void) fn();
}

C'è qualche motivo per annullare un valore di ritorno inutilizzato o ho ragione a pensare che sia una completa perdita di tempo?

Azione supplementare:

Bene, sembra abbastanza completo. Suppongo che sia meglio che commentare un valore di ritorno inutilizzato poiché il codice auto documentante è migliore dei commenti. Personalmente, disattiverò questi avvisi poiché è rumore non necessario.

Mangerò le mie parole se un insetto scappa a causa di esso ...

Risposte:


79

La risposta di David copre più o meno la motivazione per questo, per mostrare esplicitamente ad altri "sviluppatori" che sai che questa funzione restituisce ma la stai esplicitamente ignorando.

Questo è un modo per garantire che, ove necessario, i codici di errore vengano sempre gestiti.

Penso che per C ++ questo sia probabilmente l'unico posto in cui preferisco usare anche i cast in stile C, dal momento che usare la notazione cast statica completa sembra eccessivo qui. Infine, se stai rivedendo uno standard di codifica o ne stai scrivendo uno, è anche una buona idea affermare esplicitamente che anche le chiamate a operatori sovraccarichi (che non utilizzano la notazione di chiamata di funzione) dovrebbero essere esentate da questo:

class A {};
A operator+(A const &, A const &);

int main () {
  A a;
  a + a;                 // Not a problem
  (void)operator+(a,a);  // Using function call notation - so add the cast.

questo è l'unico posto in cui preferisco anche il cast in stile C. anche se nel mio standard di codifica, aggiungerei (void) a "a + a;" troppo (opportunamente parened, ovviamente: p)
Johannes Schaub - litb

8
Un po 'fuori tema, ma perché mai dovresti fare "a + a;" come quello senza usare il valore di ritorno? Mi sembra davvero un abuso di effetti collaterali e offusca l'intento del codice.
Rob K

5
Bene, quello che userete ogni giorno sarà "a = a". Ciò restituisce l'assegnazione all'oggetto (per tutte le classi che si comportano bene che è! :)). Un altro esempio è: os << "Hello World" << std :: endl. Ciascuno di essi restituisce l'oggetto "os".
Richard Corden

46

Al lavoro lo usiamo per riconoscere che la funzione ha un valore di ritorno ma lo sviluppatore ha affermato che è sicuro ignorarlo. Dato che hai etichettato la domanda come C ++ dovresti usare static_cast :

static_cast<void>(fn());

Per quanto il compilatore esegue il casting del valore restituito su void, ha poco significato.


Sopprime l'avviso sui valori restituiti inutilizzati?
Paul Tomblin,

No non lo fa. @ Mykola: con GCC4 puoi allegare un attributo che dice che il valore restituito non deve essere ignorato, il che attiverà un avviso.
David Holm

2
Uso g ++ e dà un avvertimento anche con tale cast.
klew

2
quale opzione di avviso usi? -Il valore Wunused non attiva l'avviso nel mio ambiente
Mykola Golubyev

In VC ++, sopprime l'avviso
jalf

39

La vera ragione per farlo risale a uno strumento utilizzato su codice C, chiamato lint .

Analizza il codice alla ricerca di possibili problemi ed emette avvisi e suggerimenti. Se una funzione restituisce un valore che non è stato controllato, lintavvisa nel caso in cui ciò fosse accidentale. Per mettere a tacere lintquesto avviso, devi inviare la chiamata a (void).


2
Potrebbe essere iniziato in questo modo, ma la maggior parte degli strumenti ora dispone di altri meccanismi per sopprimere avvisi come questo. Inoltre, indipendentemente dal motivo per cui questo è iniziato, in particolare nell'ambito del codice critico per la sicurezza, questo è diventato il modo usuale per "documentare" le intenzioni degli sviluppatori.
Richard Corden

5
GCC fornisce un avviso per questo con -Wall.
dissolvenza grigia

23

Trasmissione a void viene utilizzato per sopprimere gli avvisi del compilatore per variabili inutilizzate e valori o espressioni restituiti non salvati.

Lo Standard (2003) dice in §5.2.9 / 4 dice,

Qualsiasi espressione può essere convertita in modo esplicito nel tipo "cv void". Il valore dell'espressione viene scartato .

Quindi puoi scrivere:

//suppressing unused variable warnings
static_cast<void>(unusedVar);
static_cast<const void>(unusedVar);
static_cast<volatile void>(unusedVar);

//suppressing return value warnings
static_cast<void>(fn());
static_cast<const void>(fn());
static_cast<volatile void>(fn());

//suppressing unsaved expressions
static_cast<void>(a + b * 10);
static_cast<const void>( x &&y || z);
static_cast<volatile void>( m | n + fn());

Tutti i moduli sono validi. Di solito lo accorcio come:

//suppressing  expressions
(void)(unusedVar);
(void)(fn());
(void)(x &&y || z);

Va anche bene.


1
Tranne che non sempre disattiva gli avvisi del compilatore. gcc non sembra rispondere al casting per void
Matt

@ Matt: quale versione di GCC stai usando? Puoi pubblicare il tuo codice su ideone.com o stacked-crooked.com (quest'ultimo è meglio).
Nawaz

1
La dicitura standard "scartato" implica che l'avvertimento non dovrebbe essere lanciato dai linter?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

2
@CiroSantilli 巴拿馬 文件 六四 事件 法轮功: .... questa è una domanda interessante. Attualmente, non conosco la risposta a questo. Per favore, condividilo con me se lo conosci comunque.
Nawaz


4

Cast to void è gratuito. Sono solo informazioni per il compilatore su come trattarlo.


1
È "a costo zero" se il tempo per scriverlo e il tempo successivo per leggere, comprendere (e quindi ignorare) il codice è gratuito. IMO, scrivere (void)mi costa tempo ed energia e mi chiedo se l'autore sapesse come funzionano le espressioni.
wallyk

4

Per la funzionalità del tuo programma, il casting to void non ha senso. Direi anche che non dovresti usarlo per segnalare qualcosa alla persona che sta leggendo il codice, come suggerito nella risposta da David. Se vuoi comunicare qualcosa sulle tue intenzioni, è meglio usare un commento. L'aggiunta di un cast come questo sembrerà solo strano e solleverà domande sul possibile motivo. Solo la mia opinione...


2
Questo è un modello noto per dire esplicitamente agli altri che non ti interessa il valore restituito, che non ti sei semplicemente dimenticato di gestirlo.
Spidey

For the functionality of you program casting to void is meaninglessPer l'auto-documentazione e il numero di avvisi durante la compilazione, in realtà non lo è. you should not use it to signal something to the person that is reading the code [...] it is better to use a comment.Il miglior commento è nessun commento , quando il codice si spiega da solo. E, ancora una volta, mantiene un compilatore validamente avvisato felice nel processo.
underscore_d

"L'aggiunta di un cast come questo sembrerà solo strano e solleverà interrogativi sulla possibile ragione". Ho scoperto uno di questi, ed è per questo che sto leggendo questo. Sembrava un misterioso abuso di stack al mio cervello inesperto.
mynameisnafe

1

Inoltre, quando si verifica che il codice sia conforme agli standard MISTA (o altri), strumenti automatici come LDRA non ti consentiranno di chiamare una funzione che ha un tipo di ritorno senza che restituisca un valore a meno che tu non esegua esplicitamente il cast del valore restituito su (void)


0

C ++ 17 [[nodiscard]]

C ++ 17 ha standardizzato il "valore di ritorno ignorato business" con un attributo.

Pertanto, spero che le implementazioni conformi avvisino sempre solo quando nodiscard viene fornito e non avvertano mai altrimenti.

Esempio:

main.cpp

[[nodiscard]] int f() {
    return 1;
}

int main() {
    f();
}

compilare:

g++ -std=c++17 -ggdb3 -O0 -Wall -Wextra -pedantic -o main.out main.cpp

risultato:

main.cpp: In function int main()’:
main.cpp:6:6: warning: ignoring return value of int f()’, declared with attribute nodiscard [-Wunused-result]
    6 |     f();
      |     ~^~
main.cpp:1:19: note: declared here
    1 | [[nodiscard]] int f() {
      | 

Tutto ciò che segue evita l'avvertimento:

(void)f();
[[maybe_unused]] int i = f();

Non sono riuscito a utilizzare maybe_unuseddirettamente durante la f()chiamata:

[[maybe_unused]] f();

dà:

main.cpp: In function int main()’:
main.cpp:6:5: warning: attributes at the beginning of statement are ignored [-Wattributes]
    6 |     [[maybe_unused]] f();
      |     ^~~~~~~~~~~~~~~~

Il (void)cast working non sembra essere obbligatorio ma è "incoraggiato" nello standard: come posso scartare intenzionalmente un valore di ritorno [[nodiscard]]?

Inoltre, come si vede dal messaggio di avviso, una "soluzione" all'avviso è aggiungere -Wno-unused-result:

g++ -std=c++17 -ggdb3 -O0 -Wall -Wextra -pedantic -Wno-unused-result -o main.out main.cpp

anche se ovviamente non consiglierei di ignorare gli avvisi a livello globale come questo.

C ++ 20 permette anche di aggiungere un motivo per il nodiscardquale in [[nodiscard("reason")]]come accennato: https://en.cppreference.com/w/cpp/language/attributes/nodiscard

warn_unused_resultAttributo GCC

Prima della standardizzazione di [[nodiscard]], e per C prima che decidessero finalmente di standardizzare gli attributi, GCC implementava la stessa identica funzionalità con warn_unused_result:

int f() __attribute__ ((warn_unused_result));

int f() {
    return 1;
}

int main() {
    f();
}

che dà:

main.cpp: In function int main()’:
main.cpp:8:6: warning: ignoring return value of int f()’, declared with attribute warn_unused_result [-Wunused-result]
    8 |     f();
      |     ~^~

Va notato quindi che poiché ANSI C non ha uno standard per questo, ANSI C non specifica quali funzioni della libreria standard C hanno l'attributo o meno e quindi le implementazioni hanno preso le proprie decisioni su cosa dovrebbe o non essere contrassegnato con warn_unuesd_result, quale Questo è il motivo per cui in generale dovresti usare il (void)cast per ignorare i ritorni di qualsiasi chiamata a funzioni di libreria standard per evitare completamente gli avvisi in qualsiasi implementazione.

Testato in GCC 9.2.1, Ubuntu 19.10.

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.