Wie kann ich eine Warnung vor nicht verwendeten Variablen am besten zum Schweigen bringen?

236

Ich habe eine plattformübergreifende Anwendung und in einigen meiner Funktionen werden nicht alle an Funktionen übergebenen Werte verwendet. Daher erhalte ich eine Warnung von GCC, dass es nicht verwendete Variablen gibt.

Was wäre der beste Weg, um die Warnung zu codieren?

Ein #ifdef um die Funktion?

#ifdef _MSC_VER
void ProcessOps::sendToExternalApp(QString sAppName, QString sImagePath, qreal qrLeft, qreal qrTop, qreal qrWidth, qreal qrHeight)
#else
void ProcessOps::sendToExternalApp(QString sAppName, QString sImagePath, qreal /*qrLeft*/, qreal /*qrTop*/, qreal /*qrWidth*/, qreal /*qrHeight*/)
#endif
{

Das ist so hässlich, scheint aber so, wie es der Compiler bevorzugen würde.

Oder weise ich der Variablen am Ende der Funktion Null zu? (was ich hasse, weil es etwas im Programmablauf ändert, um eine Compiler-Warnung zum Schweigen zu bringen).

Gibt es einen richtigen Weg?

Phil Hannent
quelle
7
Ich habe gerade festgestellt, dass Sie letzten November eine ähnliche Frage gestellt haben. Deshalb kommt es mir bekannt vor! ;) stackoverflow.com/questions/308277/…
Alex B
9
Warum nicht einfach für beide Compiler auskommentieren? Wenn das Argument auf einem nicht verwendet wird, wird es auf dem anderen wahrscheinlich nicht verwendet werden ...
Roger Lipscombe
12
Sie sollten wissen, dass Qt nur dafür ein Q_UNUSEDMakro hat. Überprüfen Sie es in der Dokumentation.
Evan Teran
1
Die C-Lösung funktioniert auch in C ++: stackoverflow.com/a/3599170/1904815
JonnyJD
-Wno-unused-parameter kann auch eine Option sein, wenn Sie compilerspezifische Build-Flags haben können
Code Abominator

Antworten:

326

Sie können es in den (void)var;Ausdruck " " setzen (tut nichts), damit ein Compiler sieht, dass es verwendet wird. Dies ist zwischen Compilern portierbar.

Z.B

void foo(int param1, int param2)
{
    (void)param2;
    bar(param1);
}

Oder,

#define UNUSED(expr) do { (void)(expr); } while (0)
...

void foo(int param1, int param2)
{
    UNUSED(param2);
    bar(param1);
}
Alex B.
quelle
22
+1 - trotzdem würde ich dokumentieren, warum Sie die Variable nicht verwenden, auch wenn sie vorhanden ist.
Tobias Langner
18
So wird Q_UNUSEDprinzipiell umgesetzt.
Dmitry Volosnykh
11
@Cameron Sie können den Parameternamen in C ++ einfach weglassen. Wenn es als Vorlage verwendet wird, wird es in C nicht verwendet, sodass Sie den Cast-to-Void-Trick nicht benötigen.
Alex B
13
Sollte einfach auch #define UNUSED(expr) (void)(expr)funktionieren (ohne das Do-While).
JonnyJD
7
Ich frage mich, wie man das für eine variadische Vorlage macht. In template<typename... Args> void f(const Args&... args)kann ich nicht schreiben (void)args;oder (void)args...;weil beide Syntaxfehler sind.
Panzi
101

In GCC und Clang können Sie die __attribute__((unused))Präprozessor-Direktive verwenden, um Ihr Ziel zu erreichen.
Beispielsweise:

int foo (__attribute__((unused)) int bar) {
   return 0;
}
ezpz
quelle
1
Dies ist die beste Lösung für Rückruffunktionen.
Sonic Atom
1
Auch unterstützt von clang: clang.llvm.org/docs/…
Alexander
39

Ihre aktuelle Lösung ist am besten - kommentieren Sie den Parameternamen aus, wenn Sie ihn nicht verwenden. Dies gilt für alle Compiler, sodass Sie den Vorprozessor nicht speziell für GCC verwenden müssen.

Alex Kribbeln
quelle
7
Um diese Antwort zu verstärken, benötigen Sie das #ifdef nicht. Kommentieren Sie einfach die nicht verwendeten Parameternamen aus.
Quamrana
4
Ich habe einen Fall, in dem der Parameter Teil eines Rückrufs ist und das Auskommentieren die Kompilierung unterbricht (ich bin mir also nicht sicher, warum ich g++davor warne). Was würden Sie in einem solchen Fall empfehlen?
Drew Noakes
1
Stellen Sie sich eine virtuelle Inline-Methode mit nicht verwendeten Parametern / * kommentiert * / vor. Der Client der Schnittstelle sieht den Parameternamen während der automatischen Vervollständigung in den meisten IDEs nicht. In diesem Fall ist die UNUSED () - Lösung bequemer, wenn auch weniger sauber.
Cbuchart
Ich denke, einfacher ist besser, das
Auskommentieren
26

C ++ 17 Update

In C ++ 17 erhalten wir das Attribut [[vielleicht_unused]], das in [dcl.attr.unused] behandelt wird.

Das Attribut-Token Maybe_unused gibt an, dass ein Name oder eine Entität möglicherweise absichtlich nicht verwendet wird. Es darf höchstens einmal in jeder Attributliste erscheinen und es darf keine Attributargumentklausel vorhanden sein. ...

Beispiel:

 [[maybe_unused]] void f([[maybe_unused]] bool thing1,
                        [[maybe_unused]] bool thing2) {
  [[maybe_unused]] bool b = thing1 && thing2;
    assert(b);
 }

Implementierungen sollten nicht warnen, dass b nicht verwendet wird, unabhängig davon, ob NDEBUG definiert ist oder nicht. - Beispiel beenden]

Für das folgende Beispiel:

int foo ( int bar) {
    bool unused_bool ;
    return 0;
}

Sowohl clang als auch gcc generieren eine Diagnose mit -Wall -Wextra für bar und unused_bool ( Live anzeigen ).

Beim Hinzufügen von [[vielleicht_unused]] wird die Diagnose stummgeschaltet :

int foo ([[maybe_unused]] int bar) {
    [[maybe_unused]] bool unused_bool ;
    return 0;
}

sehe es live .

Vor C ++ 17

In C ++ 11 könnte eine alternative Form des UNUSEDMakros unter Verwendung eines Lambda-Ausdrucks ( über Ben Deane ) mit einer Erfassung der nicht verwendeten Variablen gebildet werden:

#define UNUSED(x) [&x]{}()

Der sofortige Aufruf des Lambda-Ausdrucks sollte anhand des folgenden Beispiels optimiert werden:

int foo (int bar) {
    UNUSED(bar) ;
    return 0;
}

wir können in godbolt sehen, dass der Anruf weg optimiert ist:

foo(int):
xorl    %eax, %eax
ret
Shafik Yaghmour
quelle
5
Sie erwähnen also C ++ 11 und schaffen es dann, ein Makro zu präsentieren?! Autsch! Vielleicht wäre die Verwendung einer Funktion sauberer? template <class T> inline void NOTUSED( T const & result ) { static_cast<void>(result); }Sie könnten auch ein Lambda in der Funktion verwenden, nehme ich an.
Alexis Wilke
Godbolt ist eine großartige Ressource
Yano
5
[&x]{}()bringt die Warnung nicht wirklich zum Schweigen, sondern leitet die Warnung stattdessen von der Anruferfunktion an das Lambda weiter. Es wird einige Zeit dauern, bis Compiler dies als Warnung erkennen, aber Clang-Tidy beschwert sich bereits über eine nicht verwendete Variable in der Erfassungsliste.
NVXX
25

Noch sauberer ist es, nur Variablennamen auskommentieren:

int main(int /* argc */, char const** /* argv */) {
  return 0;
}
Marcin Wyszynski
quelle
8
Dies ist nicht gut, wenn Sie Sauerstoff haben und die Parameter dokumentieren möchten.
Alexis Wilke
18
@AlexisWilke: Das würde sich als Fehler in Sauerstoff qualifizieren, IMO
6502
3
Sie können YOUR_PROJECT_UNUSED (argname) unter der Bedingung #ifdef DOXYGEN definieren, damit doxygen den Namen sehen kann und der echte Compiler dies nicht über int main (int YOUR_PROJECT_UNUSED (argc), ...). Nicht fabelhaft, funktioniert aber.
Mabraham
Ich finde es sehr schmerzhaft, einen Codeblock mit vielen solchen verschachtelten Kommentaren zu kommentieren. (Der Compiler beschwert sich über jeden).
Jeff McClintock
@ JeffMcClintock verwendet nur einzeilige Kommentare. Die meisten anständigen Editoren unterstützen die vertikale Blockbearbeitung (z. B. [Strg] + [V] in Vim). Verwenden Sie andernfalls #if 0 / #endifBlockkommentare.
Ruslan
24

Ein Mitarbeiter hat mich gerade auf dieses nette kleine Makro hier hingewiesen

Der Einfachheit halber werde ich das Makro unten einfügen.

#ifdef UNUSED
#elif defined(__GNUC__) 
# define UNUSED(x) UNUSED_ ## x __attribute__((unused)) 
#elif defined(__LCLINT__) 
# define UNUSED(x) /*@unused@*/ x 
#else 
# define UNUSED(x) x 
#endif

void dcc_mon_siginfo_handler(int UNUSED(whatsig))

quelle
12
"nice" "macro" "c ++" - wählen Sie 2.
Jeff McClintock
23

kennzeichnet diese Warnungen standardmäßig nicht. Diese Warnung muss entweder explizit durch Übergabe -Wunused-parameteran den Compiler oder implizit durch Übergabe -Wall -Wextra(oder möglicherweise eine andere Kombination von Flags) aktiviert worden sein.

Nicht verwendete Parameterwarnungen können einfach durch Übergabe -Wno-unused-parameteran den Compiler unterdrückt werden. Beachten Sie jedoch, dass dieses Deaktivierungsflag nach möglichen Aktivierungsflags für diese Warnung in der Compiler-Befehlszeile stehen muss, damit es wirksam wird.

Digitales Trauma
quelle
2
Auch wenn dies möglicherweise nicht die beste Antwort auf die Frage ist (da die Frage lautete, wie die Warnung vermieden und nicht deaktiviert werden kann), könnte dies die Antwort sein, nach der Leute von Google (wie ich) gesucht haben ("wie") um diese Warnung zu deaktivieren "). Also gebe ich +1, danke für deine Antwort!
Mozzbozz
13

makellose und tragbare Methode, um einen oder mehrere Parameter als nicht verwendet zu deklarieren :

template <typename... Args> inline void unused(Args&&...) {}

int main(int argc, char* argv[])
{
    unused(argc, argv);
    return 0;
}
Philippe
quelle
Sehr gut, aber beachten Sie, dass dies C ++ 11 erfordert (oder natürlich neuer).
Paul R
Ich habe diese Antwort abgelehnt, weil ich nicht auf die Kompilierungszeit (mithilfe von Vorlagen) verzichten möchte, um die Warnung zu entfernen.
Konrad Kleine
@KonradKleine: Wie viel Kompilierungszeit könnte dies möglicherweise in Anspruch nehmen? Beim Testen auf meinem Computer kann ich in einem Zehntel der Sekunde tausend dieser nicht verwendeten () Aufrufe ausführen.
Daniel McLaury
@ DanielMcLaury das war nur meine Vermutung und ich habe keine Experimente gemacht.
Konrad Kleine
8

Die Verwendung von Präprozessor-Direktiven wird die meiste Zeit als böse angesehen. Idealerweise möchten Sie sie wie die Pest vermeiden. Denken Sie daran, dass es einfach ist, den Compiler dazu zu bringen, Ihren Code zu verstehen, und dass es für andere Programmierer viel schwieriger ist, Ihren Code zu verstehen. Ein paar Dutzend solcher Fälle hier und da machen es sehr schwer, später für sich selbst oder für andere zu lesen.

Eine Möglichkeit könnte darin bestehen, Ihre Parameter in einer Art Argumentklasse zusammenzufassen. Sie können dann nur eine Teilmenge der Variablen verwenden (entspricht wirklich Ihrer Zuweisung von 0) oder für jede Plattform unterschiedliche Spezialisierungen dieser Argumentklasse haben. Dies lohnt sich jedoch möglicherweise nicht. Sie müssen analysieren, ob es passt.

Wenn Sie unmögliche Vorlagen lesen können, finden Sie möglicherweise erweiterte Tipps im Buch "Außergewöhnliches C ++". Wenn die Leute, die Ihren Code lesen würden, ihre Fähigkeiten dazu bringen könnten, die verrückten Dinge zu erfassen, die in diesem Buch gelehrt werden, dann hätten Sie einen schönen Code, der auch leicht zu lesen ist. Der Compiler weiß auch genau, was Sie tun (anstatt alles durch Vorverarbeitung auszublenden).

Ben Dadsetan
quelle
5
"Die Verwendung von Präprozessor-Direktiven wird die meiste Zeit als böse angesehen." "Ja wirklich?" Von wem?
Graeme Perrow
12
Von jedem, der sich um den Umfang, das ordnungsgemäße Debuggen oder die Vernunft kümmert.
Bill
2
@ Graeme, es sieht unschuldig aus, wenn wir nur 4 Zeilen davon sehen, aber wenn wir es verteilen, verursacht es Kopfschmerzen. Mit #ifdef können Sie grundsätzlich mehrere Versionen eines Quellcodes einfügen, von denen der Compiler nur eine sieht. Wie Bill erwähnt, erschwert es auch das Debuggen. Ich habe in verschiedenen Büchern und Blogs über die Bösartigkeit von Präprozessor-Direktiven gelesen und sie selbst erlebt. Natürlich ist alles relativ. Manchmal sind Präprozessoranweisungen einfach sinnvoll, weil alles andere schlimmere Konsequenzen hätte, und ich möchte hier nur darauf hinweisen, dass dies nach Möglichkeit vermieden werden sollte.
Ben Dadsetan
1
Überbeanspruchung ist schlecht, aber ich würde #define UNUSED(expr) (void)(expr)angemessen nennen .
JonnyJD
7

Zunächst wird die Warnung durch die Variablendefinition in der Quelldatei und nicht durch die Headerdatei generiert. Der Header kann makellos bleiben und sollte, da Sie möglicherweise etwas wie Sauerstoff verwenden, um die API-Dokumentation zu generieren.

Ich gehe davon aus, dass Sie eine völlig andere Implementierung in Quelldateien haben. In diesen Fällen können Sie entweder den fehlerhaften Parameter auskommentieren oder einfach den Parameter schreiben.

Beispiel:

func(int a, int b)
{
    b;
    foo(a);
}

Dies mag kryptisch erscheinen, also definiert man ein Makro wie UNUSED. MFC hat es so gemacht:

#ifdef _DEBUG
#define UNUSED(x)
#else
#define UNUSED(x) x
#endif

So sehen Sie die Warnung noch in Debug-Builds, könnte hilfreich sein.

Rioki
quelle
4

Ist es nicht sicher, Parameternamen immer auskommentieren? Wenn nicht, können Sie so etwas tun

#ifdef _MSC_VER
# define P_(n) n
#else
# define P_(n)
#endif

void ProcessOps::sendToExternalApp(
    QString sAppName, QString sImagePath,
    qreal P_(qrLeft), qreal P_(qrTop), qreal P_(qrWidth), qreal P_(qrHeight))

Es ist ein bisschen weniger hässlich.

Michael Krelin - Hacker
quelle
4
Die Tatsache, dass der Parametername in C ++ nicht obligatorisch ist - er ist in C - dient nur dazu, eine standardmäßige und einfache Möglichkeit zu bieten, die Warnung zu verhindern.
AProgrammer
1
@ Hacker, habe es nie gesagt. Ich neige dazu, auf Unterschiede zwischen C und C ++ hinzuweisen, insbesondere wenn sie sich in Regionen befinden, von denen Sie denken, dass sie die übliche Teilmenge sind ... Nur eine Gewohnheit, weil ich an einer gemischten Codebasis arbeite.
AProgrammer
3

Mit einem UNREFERENCED_PARAMETER(p)könnte funktionieren. Ich weiß, dass es in WinNT.h für Windows-Systeme definiert ist und auch für gcc leicht definiert werden kann (falls es noch nicht vorhanden ist).

UNREFERENCED PARAMETER(p) ist definiert als

#define UNREFERENCED_PARAMETER(P)          (P)

in WinNT.h.

Joshua
quelle
3

Ich habe dies gesehen, anstatt (void)param2die Warnung zum Schweigen zu bringen:

void foo(int param1, int param2)
{
    std::ignore = param2;
    bar(param1);
}

Sieht so aus, als wäre dies in C ++ 11 hinzugefügt worden

Krupan
quelle
Es scheint etwas zu tun, das nach dem Kompilieren nicht ignoriert wird.
GyuHyeon Choi
2

Verwenden Sie das Compiler-Flag, z. B. das Flag für GCC: -Wno-unused-variable

Stapler
quelle
1

Sie können __unuseddem Compiler mitteilen, dass die Variable möglicherweise nicht verwendet wird.

- (void)myMethod:(__unused NSObject *)theObject    
{
    // there will be no warning about `theObject`, because you wrote `__unused`

    __unused int theInt = 0;
    // there will be no warning, but you are still able to use `theInt` in the future
}
OlDor
quelle
2
Welcher Compiler? Weil __unusedes kein Standard-C ++ ist und mehr auf den Punkt gebracht wird, ist es auch nicht das, was Sie gepostet haben ... Das ist Objective-C. Diese Antwort ist also nur für bestimmte Compiler wirklich nützlich und macht den Code nicht portierbar und in der Tat nicht wirklich gültig, da Benutzercode keine Bezeichner verwenden soll __, die mit beginnen und für die Implementierung reserviert sind.
underscore_d
1

In C ++ 11 ist dies die Lösung, die ich verwende:

template<typename... Ts> inline void Unreferenced(Ts&&...) {}

int Foo(int bar) 
{
    Unreferenced(bar);
    return 0;
}

int Foo2(int bar1, int bar2) 
{
    Unreferenced(bar1, bar2);
    return 0;
}

Es wurde bestätigt, dass es portabel ist (zumindest auf modernen MSVC-, Clang- und GCC-Geräten) und keinen zusätzlichen Code erzeugt, wenn Optimierungen aktiviert sind. Ohne Optimierung wird der zusätzliche Funktionsaufruf ausgeführt und Verweise auf die Parameter werden in den Stapel kopiert, es sind jedoch keine Makros beteiligt.

Wenn der zusätzliche Code ein Problem darstellt, können Sie stattdessen diese Deklaration verwenden:

(decltype(Unreferenced(bar1, bar2)))0;

Zu diesem Zeitpunkt bietet ein Makro jedoch eine bessere Lesbarkeit:

#define UNREFERENCED(...) { (decltype(Unreferenced(__VA_ARGS__)))0; }
Nampo
quelle
1

Dies funktioniert gut, erfordert jedoch C ++ 11

template <typename ...Args>
void unused(Args&& ...args)
{
  (void)(sizeof...(args));
}
Gian Lorenzo Meocci
quelle
1
Was ist mit C ++ 14 und würde in C ++ 11 nicht funktionieren? Ich kann nichts sehen Es wird auch davon abgeraten, ALLCAPSetwas anderes als Makros zu verwenden, um sie hässlich und unerwünscht aussehen zu lassen, aber das ist wirklich nichts Schlechtes, außer dass a static_castschöner wäre.
underscore_d
0

Ich habe festgestellt, dass die meisten der vorgestellten Antworten nur für lokale nicht verwendete Variablen funktionieren und Kompilierungsfehler für nicht verwendete statische globale Variablen verursachen.

Ein weiteres Makro musste die Warnung vor nicht verwendeten statischen globalen Variablen unterdrücken.

template <typename T>
const T* UNUSED_VARIABLE(const T& dummy) { 
    return &dummy;
}
#define UNUSED_GLOBAL_VARIABLE(x) namespace {\
    const auto dummy = UNUSED_VARIABLE(x);\
}

static int a = 0;
UNUSED_GLOBAL_VARIABLE(a);

int main ()
{
    int b = 3;
    UNUSED_VARIABLE(b);
    return 0;
}

Dies funktioniert, da für nicht statische globale Variablen im anonymen Namespace keine Warnung gemeldet wird.

C ++ 11 ist jedoch erforderlich

 g++  -Wall -O3  -std=c++11 test.cpp
FaceBro
quelle
0

Lol! Ich glaube nicht, dass es auf SO eine andere Frage gibt, die alle vom Chaos korrumpierten Ketzer besser enthüllt als diese!

Bei allem Respekt vor C ++ 17 gibt es in den C ++ - Kernrichtlinien eine klare Richtlinie . AFAIR, im Jahr 2009 war diese Option genauso verfügbar wie heute. Und wenn jemand sagt, dass es in Doxygen als Fehler angesehen wird, dann gibt es in Doxygen einen Fehler

kreuzerkrieg
quelle
-14

Ich sehe Ihr Problem mit der Warnung nicht. Dokumentieren Sie im Methoden- / Funktionsheader, dass der Compiler xy hier eine (korrekte) Warnung ausgibt, diese Variablen jedoch für die Plattform z benötigt werden.

Die Warnung ist korrekt und muss nicht ausgeschaltet werden. Es macht das Programm nicht ungültig - aber es sollte dokumentiert werden, dass es einen Grund gibt.

Tobias Langner
quelle
20
Das Problem ist, dass Sie bei Hunderten oder Tausenden solcher Warnungen möglicherweise die nützliche verpassen. (Zweimal war ich in der Lage, mehrere zehntausend Warnungen durchzugehen, die meisten zu beseitigen und einige wirklich nützliche zu finden, die auf schwerwiegende Fehler hindeuteten.) Es ist immer gut, ohne Warnungen zu kompilieren, wenn möglich auf der höchsten Warnstufe.
sbi
4
In einem Projekt, an dem ich letztes Jahr gearbeitet habe, habe ich die höchste Warnstufe aktiviert und ~ 10.000 Warnungen erhalten. Nur ein paar Dutzend waren wirklich hilfreich. Unter denen waren etwa ein Dutzend wirklich fiese Bugs versteckt, aber es dauerte Wochen mehrerer Männer die Code - Basis bis zu dem Punkt zu reinigen , wo man tatsächlich konnte sehen , die wenige ernsteren. Wäre die Warnstufe ständig gestiegen und die Codebasis warnungsfrei gehalten worden, hätten sich diese Fehler niemals in den Code eingeschlichen.
sbi
1
Entschuldigung - aber die statische Code-Analyse (mit jedem verfügbaren Tool, auch wenn es nur der Compiler ist) spät im Projekt durchzuführen, ist ein bisschen wie das Programmieren des gesamten Programms. Wenn Sie fertig sind, drücken Sie Kompilieren und hoffen, dass Sie keine Fehler haben.
Tobias Langner
2
@ Richard: Ich habe an Projekten mit Tausenden von Quelldateien gearbeitet. Eine kleine Warnung hier und da, auch gut dokumentierte, summiert sich schnell. Selbst wenn während eines Builds nur Dutzende von Warnungen angezeigt werden (anstelle von Hunderten oder Tausenden), ist es zu zeitaufwändig und letztendlich gewinnbringend, sie einzeln nachschlagen zu müssen, um festzustellen, ob es sich um neue oder dokumentierte handelt. ' nicht getan werden. Dafür: Kompilieren Sie auf der höchstmöglichen Warnstufe mit null Warnungen. Jede Warnung, die auftaucht, wird sofort bemerkt, angeschaut und entweder behoben oder unterdrückt.
sbi
2
@sbi: Das Turinieren auf der höchsten Warnstufe für Ihren Compiler ist eine Form der statischen Code-Analyse. Bei der statischen Code-Analyse wird der Code nur gelesen, ohne ausgeführt und Informationen daraus abgezogen. Genau das macht der Compiler, wenn er seine Regeln auf Warnungen überprüft.
Tobias Langner