Trattare con C ++ & # 8220; inizializzato ma non referenziato & # 8221; avvertimento per la distruzione di aiutanti dell'ambito?

StackOverflow https://stackoverflow.com/questions/219770

Domanda

In Visual Studio, utilizzo spesso oggetti solo per scopi RAII. Ad esempio:

ScopeGuard close_guard = MakeGuard( &close_file, file );

Lo scopo di close_guard è quello di assicurarsi che il file si chiuda all'uscita dalla funzione, non venga usato da nessun'altra parte. Tuttavia, Visual Studio mi avvisa che una variabile locale " è inizializzata ma non referenziata " ;. Desidero disattivare questo avviso per questo caso specifico.

Come gestisci questo tipo di situazione? Visual Studio pensa che questo oggetto sia inutile, ma questo è sbagliato poiché ha un distruttore non banale.

Non vorrei usare una direttiva #pragma warning poiché ciò disattiverebbe questo avviso anche per motivi legittimi.

È stato utile?

Soluzione

Metodo 1: utilizza la direttiva #pragma warning .

#pragma warning consente una modifica selettiva del comportamento dei messaggi di avviso del compilatore.

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

ScopeGuard close_guard = MakeGuard( &close_file, file );

#pragma warning( pop )

Questo codice salva lo stato di avviso corrente, quindi disabilita l'avviso per un codice di avviso specifico e ripristina l'ultimo stato di avviso salvato.

Metodo 2: utilizza una soluzione alternativa come la seguente. Visual Studio sarà felice e anche tu. Questa soluzione alternativa viene utilizzata in molti esempi Microsoft e anche in altri progetti.

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

Oppure puoi creare un #define per aggirare il problema.

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

Alcuni utenti hanno dichiarato che il codice presentato non funzionerà perché ScopeGuard è un typedef. Questa ipotesi è errata.

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

  

Secondo lo standard C ++, a   riferimento inizializzato con un temporaneo   valore rende attivo quel valore temporaneo   per la durata del riferimento   stesso.

Altri suggerimenti

Se il tuo oggetto ha un distruttore non banale, Visual Studio dovrebbe non avvertirti. Il seguente codice non genera alcun avviso in VS2005 con avvisi completamente alzati (/ 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;
}

Commentando il distruttore dà un avvertimento; il codice così com'è non lo è.

Usiamo:

static_cast<void>(close_guard);

per le variabili di cui il compilatore si lamenta.

In alcuni file di intestazione VC ++, MS definisce una macro:

#define UNUSED(x) x

usato come:

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

Che mette a tacere l'avvertimento e lo documenta.

Userei la macro completamente in questo caso:

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

ora il tuo codice è carino e breve:

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

Un vantaggio di questo approccio è che in seguito è possibile aggiungere __LINE__ , __func__ ecc. per registrare successivamente le azioni di guardia, se necessario.

Bene, in questo caso ScopeGuard è in realtà un typedef di un tipo di riferimento. Purtroppo non funzionerebbe.

Non significherebbe che l'intero ScopeGuard non funziona, perché in quel caso il distruttore non verrà chiamato ???

Puoi impostare l'ambito dell'avviso #pragma attorno a quella riga di codice solo usando

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

o

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

Puoi anche usare UNREFERENCED_PARAMETER (close_guard); dopo la riga di codice sopra.

Immagino, in pratica, andrei a malincuore con la disabilitazione di #pragma ... o 'UNUSED'. Tuttavia, come regola principale, il codice dovrebbe essere tenuto pulito dagli avvisi anche a scapito di un po 'di massa in più. Dovrebbe essere compilato in più compilatori diversi su piattaforme e sistemi operativi diversi senza avvisi. In caso contrario, il codice deve essere corretto in modo da farlo. Mantenere il codice che genera avvisi a livello di gcc -Wall non è una buona idea.

Gli avvisi del compilatore sono tuoi amici e devono essere rispettati come una questione o un principio. Anche quando ciò significa che le cose devono essere implementate in modo un po 'più voluminoso e più dettagliato. Si ripaga da solo a lungo termine poiché il codice viene portato, mantenuto e sopravvive per sempre ...

Prova ad aggiungere "volatile" alla dichiarazione ScopeGuard.

Uso il post di smink sopra e devo solo aggiungere che aggiungo un commento accanto al #define che dice // usato per sopprimere l'avvertimento [numero di avviso] in Visual Studio

Potresti creare esplicitamente l'oggetto ScopeGuardImpl1, a condizione che non ci siano così tanti parametri nei casi che stai utilizzando che il risultato è illeggibile. In questo modo eviteresti il ??riferimento inizializzato con temporaneo che l'avviso VS apparentemente non riesce a capire. Il costo è dover dire le cose a lungo, piuttosto che ottenere la magia del modello MakeGuard.

Il problema principale qui sembra essere il fatto che il compilatore non capisca esattamente cosa stai facendo ... che sembra essere usare la semantica di scoping in C ++ per ottenere un codice chiamato quando una variabile viene deallocata anche quando è non utilizzato. Destra? Lo stesso meccanismo mi sembra borderline ... un compilatore dovrebbe avere il diritto di rimuovere le variabili non utilizzate ma la semantica di costruzione C ++ rovina davvero queste cose. Nessun altro modo per farlo che è meno gioco di prestigio?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top