2010-10-27 2 views
20

In C++ wird manchmal eine Variable definiert, aber nicht verwendet. Hier ist ein Beispiel - eine Funktion für die Verwendung mit COM_INTERFACE_ENTRY_FUNC_BLIND ATL Makro:Wird ein "VariablenName;" C++ - Anweisung zu jeder Zeit ein No-Op sein?

HRESULT WINAPI blindQuery(void* /*currentObject*/, REFIID iid, void** ppv, DWORD_PTR /*param*/) 
{ 
    DEBUG_LOG(__FUNCTION__); //DEBUG_LOG macro expands to an empty string in non-debug 
    DEBUG_LOG(iid); 
    iid; // <<<<<<<----silence compiler warning 
    if(ppv == 0) { 
     return E_POINTER; 
    } 
    *ppv = 0; 
    return E_NOINTERFACE; 
} 

Im obigen Beispiel iid Parameter mit DEBUG_LOG Makro verwendet werden, die in nicht-Debug-Konfigurationen in eine leere Zeichenfolge erweitert. Das Auskommentieren oder Entfernen des Variablennamens iid in der Signatur ist daher keine Option. Wenn Nicht-Debugkonfigurationen kompiliert werden, erzeugt der Compiler eine C4100: 'iid' : unreferenced formal parameter Warnung. Um also die Warnung zum Schweigen zu bringen, wird die iid;-Anweisung hinzugefügt, von der angenommen wird, dass sie ein No-Op ist.

Die Frage ist folgende: Wenn wir eine der folgenden Erklärungen haben:

CSomeType variableName; //or 
CSomeType& variableName; //or 
CSomeType* variableName; 

wird die folgende Anweisung in C++ Code:

variableName; 

sein eine no-op zu jeder Zeit unabhängig von was CSomeType ist?

+2

Wird dadurch nicht die Warnung "Aussage hat keine Wirkung" ausgelöst? – falstro

+0

@roe: Es ist nicht auf Visual C++. Vielleicht ist es bei einigen anderen Compilern. – sharptooth

+0

Wenn zwischen der Deklaration und der Anweisung Sie den Präprozessor missbrauchen, könnte es einen Effekt haben. Zum Beispiel '#define variableName exit (-1)' – Benoit

Antwort

47

Ja, aber Sie werden wahrscheinlich eine weitere Warnung erhalten.

Der Standard Weg dies zu tun ist: (void)iid;.


Sehr technisch, dies noch iid in ein Register geladen werden konnte und nichts tun. Zugegeben, das ist im Compiler-Teil extrem doof (ich bezweifle, dass jemand das jemals tun würde, wenn es den Compiler löscht), aber es ist ein ernsthafteres Problem, wenn der zu ignorierende Ausdruck etwas ist, das beobachtbares Verhalten betrifft, wie Aufrufe von IO-Funktionen oder Lesen und Schreiben von volatile Variablen.

Dies bringt eine interessante Frage auf: Können wir einen Ausdruck nehmen und komplett ignorieren Sie es?

Das heißt, was wir jetzt haben, ist dies:

#define USE(x) (void)(x) 

// use iid in an expression to get rid of warning, but have no observable effect 
USE(iid); 

// hm, result of expression is gone but expression is still evaluated 
USE(std::cout << "hmmm" << std::endl); 

Dies liegt in der Nähe einer Lösung:

// sizeof doesn't evaluate the expression 
#define USE(x) (void)(sizeof(x)) 

Aber irgendwie:

void foo(); 

// oops, cannot take sizeof void 
USE(foo()); 

Die Lösung ist zu einfach:

// use expression as sub-expression, 
// then make type of full expression int, discard result 
#define USE(x) (void)(sizeof((x), 0)) 

Das garantiert keine Operation.

Edit: Die oben in der Tat keine Wirkung garantiert, aber ich gepostet ohne Prüfung. Beim Testen wird zumindest in MSVC 2010 erneut eine Warnung generiert, da der Wert nicht verwendet wird. Das ist nicht gut, Zeit für mehr Tricks!


Erinnerung: Wir möchten einen Ausdruck "verwenden", ohne ihn zu bewerten.Wie kann das gemacht werden? Wie folgt aus:

#define USE(x) ((void)(true ? 0 : (x))) 

Dies hat ein einfaches Problem wie beim letzten Mal (schlechter tatsächlich), dass (x) Bedürfnisse zu int konvertierbar sein. Dies ist wiederum trivial zu beheben:

#define USE(x) ((void)(true ? 0 : ((x), 0))) 

Und wir sind wieder auf gleiche Art von Wirkung, die wir letztes Mal (keine) hatten, aber diesmal x „verbraucht“, so bekommen wir keine Warnungen . Richtig gemacht?

Es ist eigentlich immer noch ein Problem mit dieser Lösung (und wurde in der letzten un-Lösung als gut, aber unbemerkt), und es kommt in diesem Beispiel folgenden:

struct foo {}; 
void operator,(const foo&, int) {} 

foo f; 
USE(f); // oops, void isn't convertible to int! 

Das heißt, wenn Der Typ des Ausdrucks (x) überlädt den Komma-Operator auf etwas, das nicht in int konvertiert werden kann, die Lösung schlägt fehl. Sicher, unwahrscheinlich, aber aus Gründen völlig über Bord gehen, können wir das Problem beheben mit:

#define USE(x) ((void)(true ? 0 : ((x), void(), 0))) 

wir am Ende wirklich mit Null stellen Sie sicher. This trick brought to you by Johannes.


auch wie bereits erwähnt, wenn die oben nicht genug war, ein dumm genug Compiler könnten möglicherweise „load“ der Ausdruck 0 (in ein Register oder etwas), dann ist es ignorieren.

Ich denke, es ist unmöglich, das loszuwerden, da wir schließlich einen Ausdruck brauchen, der zu einer Art von Art führt, die man ignorieren kann, aber wenn ich jemals darüber nachdenke, füge ich sie hinzu.

+0

Obwohl ich stimme zu, bedeutet dies nicht, dass der Ausdruck ein No-Op werden muss. Der Compiler könnte, falls gewünscht, den Wert in ein Register laden und dann ignorieren. Während dies albern wäre, ist es nicht unergründlich, dass ein optimierender Compiler manchmal Bits herumläßt. –

+0

@eda: Es ist erforderlich, nichts zu tun. Bitte beachten Sie das Zitat [hier] (http://stackoverflow.com/questions/4031228/why-is-operator-void-not-invoked-with-cast-syntax/4031487#4031487). – GManNickG

+0

Nein, der Ausdruck "Wert" wird verworfen. Das Laden des Werts in ein Register oder das anderweitige Anzeigen seiner Existenz für die CPU würde diese Regel nicht verletzen. Soweit es die Sprache betrifft, hatte es keine erkennbaren Nebenwirkungen. –

2

Nun, es ist nicht wirklich möglich, mit hundertprozentiger Sicherheit zu sagen, ohne den Quellcode des Compilers zu betrachten, aber ich wäre sehr überrascht, wenn dies jemals Code in modernen Compilern generiert hätte.

Am Ende des Tages, wenn Sie sich Sorgen um eine bestimmte Instanz machen, dann können Sie immer den generierten Assembler-Code ansehen.

Verwandte Themen