Warum nicht verwendete Rückgabewerte ungültig machen?


112
int fn();

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

Gibt es einen Grund, einen nicht verwendeten Rückgabewert für ungültig zu erklären, oder habe ich Recht, wenn ich denke, dass dies reine Zeitverschwendung ist?

Nachverfolgen:

Nun, das scheint ziemlich umfassend zu sein. Ich nehme an, es ist besser als einen nicht verwendeten Rückgabewert zu kommentieren, da selbstdokumentierender Code besser ist als Kommentare. Persönlich werde ich diese Warnungen ausschalten, da es unnötiger Lärm ist.

Ich werde meine Worte essen, wenn ein Fehler dadurch entkommt ...

Antworten:


79

Davids Antwort deckt ziemlich genau die Motivation dafür ab, anderen "Entwicklern" explizit zu zeigen, dass Sie wissen, dass diese Funktion zurückkehrt, aber Sie ignorieren sie explizit.

Auf diese Weise können Sie sicherstellen, dass bei Bedarf immer Fehlercodes behandelt werden.

Ich denke, für C ++ ist dies wahrscheinlich der einzige Ort, an dem ich auch C-Casts bevorzuge, da sich die Verwendung der vollständigen statischen Cast-Notation hier einfach wie ein Overkill anfühlt. Wenn Sie einen Codierungsstandard überprüfen oder einen schreiben, ist es auch eine gute Idee, explizit anzugeben, dass Aufrufe an überladene Operatoren (ohne Funktionsaufrufnotation) ebenfalls davon ausgenommen sein sollten:

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.

Dies ist der einzige Ort, an dem ich auch eine Besetzung im C-Stil bevorzuge. tho in meinem Codierungsstandard würde ich (void) zu "a + a" hinzufügen; auch (natürlich richtig parened: p)
Johannes Schaub - litb

8
Etwas abseits des Themas, aber warum sollten Sie jemals "a + a" machen? so ohne den Rückgabewert zu verwenden? Das scheint mir wirklich ein Missbrauch von Nebenwirkungen zu sein und verschleiert die Absicht des Codes.
Rob K

5
Nun, die, die Sie jeden Tag verwenden, ist 'a = a'. Dies gibt das zugewiesene Objekt zurück (für alle gut verhaltenen Klassen also! :)). Ein weiteres Beispiel ist: os << "Hello World" << std :: endl. Jeder von ihnen gibt das "os" -Objekt zurück.
Richard Corden

46

Bei der Arbeit verwenden wir dies, um zu bestätigen, dass die Funktion einen Rückgabewert hat, aber der Entwickler hat behauptet, dass es sicher ist, ihn zu ignorieren. Da Sie die Frage als C ++ markiert haben, sollten Sie static_cast verwenden :

static_cast<void>(fn());

Für den Compiler hat das Umwandeln des Rückgabewerts in void wenig Bedeutung.


Unterdrückt es die Warnung vor nicht verwendeten Rückgabewerten?
Paul Tomblin

Nein, tut es nicht. @Mykola: Mit GCC4 können Sie ein Attribut anhängen, das besagt, dass der Rückgabewert nicht ignoriert werden sollte, was eine Warnung auslöst.
David Holm

2
Ich benutze g ++ und es gibt auch bei solchen Darstellern eine Warnung.
Klew

2
Welche Warnoption verwenden Sie? -Wunused-Wert löst nicht die Warnung in meiner Umgebung aus
Mykola Golubyev

In VC ++ unterdrückt es die Warnung
Jalf

39

Der wahre Grund dafür liegt in einem Tool, das für C-Code verwendet wird und Lint heißt .

Es analysiert Code auf mögliche Probleme und gibt Warnungen und Vorschläge aus. Wenn eine Funktion einen Wert zurückgibt, der dann nicht überprüft wurde, wird lintgewarnt, falls dies versehentlich geschieht. Um lintdiese Warnung zum Schweigen zu bringen , rufen Sie an (void).


2
Möglicherweise hat es auf diese Weise begonnen, aber die meisten Tools verfügen jetzt über andere Mechanismen, um Warnungen wie diese zu unterdrücken. Unabhängig davon, warum dies begann, insbesondere im Bereich des sicherheitskritischen Codes, ist dies die übliche Methode, um Entwicklerabsichten zu "dokumentieren".
Richard Corden

5
GCC warnt diesbezüglich mit -Wall.
Greyfade

23

Casting to voidwird verwendet, um Compiler-Warnungen für nicht verwendete Variablen und nicht gespeicherte Rückgabewerte oder Ausdrücke zu unterdrücken.

Der Standard (2003) sagt in §5.2.9 / 4,

Jeder Ausdruck kann explizit in den Typ "cv void" konvertiert werden. Der Ausdruckswert wird verworfen .

So können Sie schreiben:

//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());

Alle Formulare sind gültig. Normalerweise mache ich es kürzer als:

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

Es ist auch okay.


1
Abgesehen davon, dass die Compiler-Warnungen nicht immer deaktiviert werden. gcc scheint nicht auf Casting zu reagieren
Matt

@Matt: Welche Version von GCC verwenden Sie? Können Sie Ihren Code auf ideone.com oder stacked-crooked.com veröffentlichen (letzteres ist besser)?
Nawaz

1
Bedeutet der Standardtext "verworfen", dass die Warnung nicht durch Linters ausgelöst werden sollte?
Ciro Santilli 法轮功 冠状 病 六四 事件 25

2
@CiroSantilli .... 文件 六四 事件 法轮功: .... das ist eine interessante Frage. Derzeit kenne ich die Antwort darauf nicht. Bitte teilen Sie mir mit, wenn Sie es trotzdem erfahren.
Nawaz

8

Seit c ++ 17 haben wir das [[maybe_unused]]Attribut, das anstelle der voidBesetzung verwendet werden kann.


1
C ++ 17 wurde ebenfalls hinzugefügt, nodiscardwas von Interesse sein könnte: stackoverflow.com/a/61675726/895245 Ich denke auch, dass Sie [[maybe_unused]]den Aufruf anscheinend nicht direkt anwenden können, sondern nur auf eine Dummy-Variable, die die Rückgabe des Aufrufs akzeptiert? Wenn das stimmt, ist es ein bisschen wackelig.
Ciro Santilli 8 冠状 病 六四 六四 8

4

Cast to void ist kostenlos. Es sind nur Informationen für den Compiler, wie er damit umgehen soll.


1
Es ist "kostenlos", wenn die Zeit zum Schreiben und das anschließende Lesen, Verstehen (und dann Ignorieren) des Codes frei ist. IMO, das Schreiben (void)kostet mich Zeit und Energie und lässt mich fragen, ob der Autor wusste, wie Ausdrücke funktionieren.
Wallyk

4

Für die Funktionalität Ihres Programms ist Casting to void bedeutungslos. Ich würde auch argumentieren, dass Sie es nicht verwenden sollten, um der Person, die den Code liest, etwas zu signalisieren, wie in der Antwort von David vorgeschlagen. Wenn Sie etwas über Ihre Absichten mitteilen möchten, ist es besser, einen Kommentar zu verwenden. Das Hinzufügen einer solchen Besetzung sieht nur seltsam aus und wirft Fragen nach dem möglichen Grund auf. Nur meine Meinung...


2
Dies ist ein bekanntes Muster, um anderen explizit mitzuteilen, dass Ihnen der Rückgabewert egal ist und dass Sie nicht einfach vergessen haben, damit umzugehen.
Spidey

For the functionality of you program casting to void is meaninglessFür die Selbstdokumentation und die Anzahl der Warnungen beim Kompilieren ist dies wirklich nicht der Fall . you should not use it to signal something to the person that is reading the code [...] it is better to use a comment.Der beste Kommentar ist kein Kommentar , wenn sich der Code selbst erklärt. Auch hier bleibt ein gültiger Warn-Compiler zufrieden.
underscore_d

"Das Hinzufügen einer solchen Besetzung wird nur seltsam aussehen und Fragen nach dem möglichen Grund aufwerfen." Ich habe eines davon entdeckt, und deshalb lese ich das hier. Für mein ungeübtes Gehirn schien es ein mysteriöser Stapelmissbrauch zu sein.
mynameisnafe

1

Wenn Sie überprüfen, ob Ihr Code den MISTA-Standards (oder anderen) entspricht, können Sie mit automatischen Tools wie LDRA keine Funktion mit einem Rückgabetyp aufrufen, ohne dass ein Wert zurückgegeben wird, es sei denn, Sie geben den zurückgegebenen Wert explizit in (void) um.


0

C ++ 17 [[nodiscard]]

C ++ 17 standardisierte das "Rückgabewert ignoriertes Geschäft" mit einem Attribut.

Daher hoffe ich, dass konforme Implementierungen immer nur dann warnen, wenn dies nodiscardangegeben ist, und niemals anders warnen.

Beispiel:

main.cpp

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

int main() {
    f();
}

kompilieren:

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

Ergebnis:

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() {
      | 

Folgendes vermeidet die Warnung:

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

Ich konnte nicht maybe_unuseddirekt auf dem f()Anruf verwenden:

[[maybe_unused]] f();

gibt:

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

Die (void)Besetzungsarbeit scheint nicht obligatorisch zu sein, wird jedoch im Standard "empfohlen ": Wie kann ich absichtlich einen Rückgabewert verwerfen?

Wie aus der Warnmeldung hervorgeht, besteht eine "Lösung" der Warnung darin, Folgendes hinzuzufügen -Wno-unused-result:

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

obwohl ich natürlich nicht empfehlen würde, Warnungen global wie diese zu ignorieren.

C ++ 20 können Sie auch einen Grund, die hinzuzufügen , nodiscardwie in [[nodiscard("reason")]]wie erwähnt unter: https://en.cppreference.com/w/cpp/language/attributes/nodiscard

GCC- warn_unused_resultAttribut

Vor der Standardisierung von [[nodiscard]]und für C, bevor sie sich endgültig für die Standardisierung von Attributen entscheiden, hat GCC genau dieselbe Funktionalität implementiert mit warn_unused_result:

int f() __attribute__ ((warn_unused_result));

int f() {
    return 1;
}

int main() {
    f();
}

was gibt:

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();
      |     ~^~

Es sollte dann darauf hingewiesen, dass seit ANSI C keinen Standard hat für diesen, ANSI C nicht angibt , welche C - Standardbibliothek Funktionen das Attribut haben oder nicht , und deshalb haben Implementierungen ihre eigenen Entscheidungen darüber , was sollte oder nicht markiert werden warn_unuesd_result, die Aus diesem Grund müssten Sie im Allgemeinen die (void)Umwandlung verwenden, um Rückgaben von Aufrufen von Standardbibliotheksfunktionen zu ignorieren und Warnungen in jeder Implementierung vollständig zu vermeiden.

Getestet in GCC 9.2.1, Ubuntu 19.10.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.