2008-10-20 3 views
13

In Visual Studio verwende ich Objekte oft nur für RAII Zwecke. Zum Beispiel:Umgang mit C++ - Warnung "initialisiert, aber nicht referenziert" zur Zerstörung von Scope-Helfern?

ScopeGuard close_guard = MakeGuard(&close_file, file); 

Der ganze Zweck der close_guard ist, um sicherzustellen, dass die Datei auf Funktion Ausgang in der Nähe sein wird, ist es nirgendwo anders verwendet wird. Visual Studio gibt jedoch eine Warnung, dass eine "lokale Variable initialisiert aber nicht auf verwiesen wird". Ich möchte diese Warnung für diesen speziellen Fall deaktivieren.

Wie gehen Sie mit dieser Situation um? Visual Studio denkt, dass dieses Objekt nutzlos ist, aber das ist falsch, da es einen nicht-trivialen Destruktor hat.

Ich würde nicht eine #pragma Warnung Direktive dafür verwenden wollen, da es diese Warnung sogar aus legitimen Gründen abstellen würde.

+0

Konnten Sie den Code für ScopeGuard und MakeGuard (oder einige cut-down-Versionen, die noch das Verhalten aufweisen) veröffentlichen? –

+0

Bitte lesen Sie http://www.ddj.com/cpp/184403758 für eine mögliche Implementierung. –

+0

In welcher Version von MSVC sehen Sie dieses Verhalten? – MSN

Antwort

5

Methode 1: Verwenden Sie die #pragma warning Richtlinie.

#pragma warning ermöglicht selektive Änderung des Verhaltens von Compiler-Warnmeldungen.

#pragma warning(push) 
#pragma warning(disable : 4705) // replace 4705 with warning number 

ScopeGuard close_guard = MakeGuard(&close_file, file); 

#pragma warning(pop) 

Dieser Code speichert die aktuelle Warnzustand, dann sperrt sie die Warnung für eine bestimmte Warncode und dann wieder die letzte Warnung Zustand gespeichert.

Methode 2: Verwenden Sie eine Problemumgehung wie folgt. Visual Studio wird sich freuen und Sie auch. Diese Problemumgehung wird in vielen Microsoft-Beispielen und auch in anderen Projekten verwendet.

ScopeGuard close_guard = MakeGuard(&close_file, file); 
close_guard; 

Oder Sie können eine #define erstellen, die Warnung zu umgehen.

#define UNUSED_VAR(VAR) VAR 
... 
ScopeGuard close_guard = MakeGuard(&close_file, file); 
UNUSED_VAR(close_guard); 

angegeben Einige Benutzer, dass der Code präsentiert wird nicht funktionieren, weil ein Scopeguard typedef ist. Diese Annahme ist falsch.

http://www.ddj.com/cpp/184403758

Nach dem C++ Standard teil, eine mit einem temporären Wert initialisiert Referenz macht, dass temporärer Wert Live für die gesamte Lebensdauer der Referenz selbst.

+0

Dies wurde in [Dobbs Journal] (http://www.ddj.com/cpp/184403758) von Andrei Alexandrescu und Petru Marginean veröffentlicht. Dies sind zwei C++ - Player mit starkem Hintergrund. Und ich habe auch nie ein solches Verhalten. Werde es überprüfen und danach kommentieren. –

+0

ScopeGuard ist in der Tat ein typedef für einen Referenztyp, aber das ist nicht falsch. Auszug aus dem Link von smink: "Gemäß dem C++ Standard wird eine Referenz, die mit einem temporären Wert initialisiert wurde, diesen temporären Wert für die Lebensdauer der Referenz selbst aktiv." –

+0

Ja, das ist der wichtige Teil in dem Artikel, der den Mechanismus funktioniert. Die Tatsache, dass ScopeGuard ein typedef ist, macht den Code nicht falsch und funktioniert immer noch. Selbst wenn das so wäre, würden wir ein anderes komplett anderes Thema diskutieren und nicht die Frage. –

0

Versuchen Sie, der ScopeGuard-Deklaration "flüchtig" hinzuzufügen.

+0

Was würde das tun? –

+0

Nichts wirklich in diesem Fall, außer möglicherweise Stille die Compiler-Warnung. – Aaron

+0

Volatile teilt dem Compiler mit, dass die Variable außerhalb ihres Wissens geändert werden kann. Ich benutze es sehr für Hardware-Register. Wenn die Variable flüchtig ist, muss der Compiler sie erstellen und zuweisen. – Robert

3

Wir verwenden:

static_cast<void>(close_guard); 

für Variablen, dass der Compiler über beschwert.

+0

Ich würde sagen, dass casting to void möglicherweise eine Situation ist, in der eine C-Style-Umwandlung in C++ über eine static_cast gerechtfertigt ist ... –

1

können Sie Rahmen der Pragma Warnung um diese Codezeile nur durch

#pragma warning(push) 
#pragma warning(disable:XXXX) 
your code here; 
#pragma warning(pop) 

oder

mit
#pragma warning(disable:XXXX) 
your code here; 
#pragma warning(default:XXXX) 

Sie können sich auch UNREFERENCED_PARAMETER(close_guard); nach der Codezeile verwenden.

3

In einigen VC++ Header-Dateien definiert MS ein Makro:

#define UNUSED(x) x 

verwendet wie:

ScopeGuard close_guard = MakeGuard(&close_file, file); 
UNUSED(close_guard); 

, die die Warnung zum Schweigen bringt, und dokumentiert diese.

+0

UNUSED_ALWAYS ist ebenfalls in den meisten Windows-Programmen definiert und funktioniert genauso. –

8

Wenn Ihr Objekt einen nicht-trivialen Destruktor hat, sollte Visual Studio nicht Ihnen diese Warnung geben. Der folgende Code erzeugt keine Warnungen in VS2005 mit Warnungen drehte den ganzen Weg nach oben (/ W4):


class Test 
{ 
public: 
    ~Test(void) { printf("destructor\n"); } 
}; 

Test foo(void) { return Test(); } 

int main(void) 
{ 
    Test t = foo(); 
    printf("moo\n"); 

    return 0; 
} 

die destructor kommentierte heraus gibt eine Warnung; der Code wie er ist nicht.

+0

Das Problem scheint zu sein, dass "ScopeGuard" eigentlich ein Referenztyp für ein Objekt mit einem nicht-trivialen Destruktor ist. VS scheint nicht "schlau genug" zu sein, um damit richtig umzugehen. –

0

Ich benutze smink der Beitrag oben und haben nur noch hinzufügen, dass ich einen Kommentar neben dem #define sagen // verwendet zur Unterdrückung dieser Warnung [Warnnummer] in Visual Studio-Stick

-3

Die Kernfrage hier scheint wirklich zu sein dass der Compiler nicht ganz versteht, was Sie tun ... was zu sein scheint, um die Scoping-Semantik in C++ zu verwenden, um Code aufzurufen, der aufgerufen wird, wenn eine Variable freigegeben wird, auch wenn sie nicht verwendet wird. Recht? Dieser Mechanismus selbst erscheint mir als Grenzlinie ... ein Compiler sollte das Recht haben, unbenutzte Variablen zu entfernen, aber die C++ Konstruktionssemantik vermasselt diese Dinge wirklich. Keine andere Möglichkeit, das zu tun, ist weniger Taschenspielertrick?

+1

RAII ist eine leistungsfähige Technik. Sie sollten wirklich darüber nachdenken zu lesen http://www.ddj.com/cpp/184403758 –

+0

Danke, das war ziemlich interessant. Aber da scheinen sie auf das erstellte Objekt im ".dismiss()" - Aufruf zuzugreifen, der es zu benutzen scheinen würde? Würde sich der VC-Compiler dann beschweren? – jakobengblom2

+0

Es würde sich natürlich nicht beschweren, wenn Sie die Funktion ".dismiss()" verwenden. Aber das ist hier nicht der Fall;) –

2

Nun, in diesem Fall ist ScopeGuard eigentlich ein Typdef zu einem Referenztyp. Das würde leider nicht funktionieren.

Würde das nicht bedeuten, dass der ganze ScopeGuard nicht funktioniert, weil in diesem Fall der Destruktor nicht aufgerufen wird ???

+0

Falls sich jemand fragt, woher es kommt. Es war ein Kommentar des OP zu einer Antwort von "Sherm Pendley", die gestrichen wurde. Ich nehme an, dass Sherm es gelöscht hat, da der Kommentar andeutete, dass seine Lösung nicht benutzbar wäre. –

+0

Nein, ich meinte, dass die Deklaration der Variablen vor ihrer Verwendung nicht funktionieren würde, da sie eigentlich eine Referenz ist. –

+0

Eigentlich ist das in Ordnung ... das Binden eines Verweises auf ein Temporäres verlängert die Lebensdauer dieses Temporärs auf das der Referenz. Beispiel: {string & s = string ("hallo") + "world!"; cout << s; } Dieser Code ist in Ordnung, weil das von Operator + zurückgegebene temporäre Leben bis zur nächsten schließenden Klammer dauert. – Aaron

1

Ich denke, in der Praxis würde ich gratuly mit der #pragma deaktivieren gehen ... oder 'unbenutzt'. Als eine Hauptregel sollte der Code jedoch selbst bei einem zusätzlichen Massenaufwand von Warnungen freigehalten werden. Es sollte in mehreren verschiedenen Compilern auf verschiedenen Plattformen und Betriebssystemen ohne Warnungen kompiliert werden. Wenn nicht, muss der Code so korrigiert werden, dass es funktioniert. Es ist keine gute Idee, Code zu pflegen, der Warnungen auf gcc -Wall-Ebene generiert.

Compilerwarnungen sind Ihr Freund und sollten als eine Angelegenheit oder ein Prinzip beachtet werden. Auch wenn es bedeutet, dass die Dinge etwas sperriger und ausführlicher implementiert werden müssen. Zahlt sich auf lange Sicht aus, da der Code portiert, verwaltet und für immer weiterlebt ...

+0

Diese Compiler-Warnung ist pedantisch. Deaktivieren Sie es sofort. Es tut weh, selbst Code in Fällen zu dokumentieren, zB: void foo (int/* numberOfWidgets * /); – Aaron

0

Sie könnten das ScopeGuardImpl1-Objekt explizit erstellen, vorausgesetzt, dass in den von Ihnen verwendeten Fällen nicht so viele Parameter vorhanden sind dass das Ergebnis nicht lesbar ist. Auf diese Weise würden Sie die Referenz-initialisiert-mit-temporär vermeiden, dass die VS-Warnung anscheinend nicht versteht. Die Kosten müssen sich auf lange Sicht buchstabieren lassen, anstatt das MakeGuard-Template magisch zu machen.

3

Ich würde verwenden Makro den ganzen Weg in diesem Fall:

#define SCOPE_GUARD(guard, fn, param) \ 
    ScopeGuard guard = MakeGuard(fn, param); \ 
    static_cast<void>(guard) 

jetzt Ihren Code ist schön kurz:

SCOPE_GUARD(g1, &file_close, file1); 
SCOPE_GUARD(g2, &file_close, file2); 

Ein Vorteil dieses Ansatzes ist, dass Sie später hinzufügen __LINE__, __func__ usw., um die Schutzmaßnahmen später bei Bedarf zu protokollieren.