Qual è il punto di g ++ -Wreorder?


150

L'opzione g ++ -Wall include -Wreorder. Quello che fa questa opzione è descritto di seguito. Non è ovvio per me perché qualcuno dovrebbe interessarsene (specialmente abbastanza per attivarlo di default in -Wall).

-Wreorder (solo C ++)
  Avvisa quando l'ordine degli inizializzatori del membro indicato nel codice no
  corrisponde all'ordine in cui devono essere eseguiti. Per esempio:

    struct A {
      int i;
      int j;
      A (): j (0), i (1) {}
    };

  Il compilatore riorganizzerà gli inizializzatori del membro per i e j in
  abbinare l'ordine delle dichiarazioni dei membri, emettendo un avvertimento a quello
  effetto. Questo avviso è abilitato da -Wall.

2
Alcune buone risposte qui, ma un breve accantonamento nel caso in cui sia di interesse per chiunque: g ++ ha una bandiera per considerare questo come un errore totale:-Werror=reorder
Max Barraclough,

Risposte:


257

Tener conto di:

struct A {
    int i;
    int j;
    A() : j(0), i(j) { }
};

Ora iè inizializzato su un valore sconosciuto, non zero.

In alternativa, l'inizializzazione di ipuò avere alcuni effetti collaterali per i quali l'ordine è importante. Per esempio

A(int n) : j(n++), i(n++) { }

80
Questo dovrebbe davvero essere l'esempio nella documentazione.
Ben S

3
Grazie. Con la maggior parte dei nostri tipi di tipi POD con semplici inizializzatori, questo non mi è venuto in mente. Il tuo esempio è molto meglio dell'esempio manuale di g ++.
Peeter Joot,

5
@Mike questo è perché il tuo compilatore (gcc) inizializza le variabili non inizializzate su 0, ma questo non è qualcosa da cui dovresti dipendere; essendo 0 è solo un effetto collaterale del valore sconosciuto per le variabili non inizializzate è 0.
ethanwu10

2
@Yakk L'ordine era pagina man-> SO risposta. Ecco un archivio della pagina man del 2007 che elenca esplicitamente questo esempio. Il commento votato da Ben S è un esempio esilarante di qualcuno che suggerisce che qualcosa esista senza nemmeno verificarlo. web.archive.org/web/20070712184121/http://linux.die.net/man/1/…
KymikoLoco

3
@KymikoLoco Questo è semplicemente sbagliato. L'esempio nella pagina man è quello dell'OP (dove iè inizializzato a 1). Qui, iè inizializzato a j, che in realtà dimostra un problema.
jazzpi,

42

Il problema è che qualcuno potrebbe vedere l'elenco degli inizializzatori membri nel costruttore e pensare che siano eseguiti in quell'ordine (prima j, poi i). Non lo sono, vengono eseguiti nell'ordine in cui i membri sono definiti nella classe.

Supponi di aver scritto A(): j(0), i(j) {}. Qualcuno potrebbe leggerlo, e pensare che io finisca con il valore 0. Non lo fa, perché lo hai inizializzato con j, che contiene junk perché non è stato inizializzato.

L'avvertimento ti ricorda di scrivere A(): i(j), j(0) {}, che si spera sia molto più sospetto.


Sembra / annusa davvero di pesce! :) Sicuramente odore di codice :) Grazie per la chiara spiegazione che è giusta per il punto. :)
Sarà il

1
"... ti ricorda di scrivere A (): i (j), j (0) {} ..." Suggerisco che ti ricordi di riordinare i membri della classe in questo caso particolare.
2.718,

18

Altre risposte hanno fornito alcuni buoni esempi che giustificano l'opzione per un avviso. Ho pensato di fornire un contesto storico. Il creatore di C ++, Bjarne Stroustrup, spiega nel suo libro Il linguaggio di programmazione C ++ (3a edizione, Pagina 259):

I costruttori dei membri vengono chiamati prima che venga eseguito il corpo del costruttore della classe contenente. I costruttori vengono chiamati nell'ordine in cui sono dichiarati nella classe anziché nell'ordine in cui compaiono nell'elenco di inizializzatori. Per evitare confusione, è meglio specificare gli inizializzatori in ordine di dichiarazione. I distruttori membri vengono chiamati nell'ordine inverso rispetto alla costruzione.


10

Questo può morderti se i tuoi inizializzatori hanno effetti collaterali. Tener conto di:

int foo() {
    puts("foo");
    return 1;
}

int bar() {
    puts("bar");
    return 2;
}

struct baz {
    int x, y;
    baz() : y(foo()), x(bar()) {}
};

Quanto sopra stamperà "bar" e poi "pippo", anche se intuitivamente si suppone che l'ordine sia come scritto nell'elenco di inizializzatori.

In alternativa, se xe ysono di un tipo definito dall'utente con un costruttore, tale costruttore può anche avere effetti collaterali, con lo stesso risultato non ovvio.

Può anche manifestarsi quando l'inizializzatore per un membro fa riferimento a un altro membro.


7

L'avvertimento esiste perché se hai appena letto il costruttore, sembra j sia stato inizializzato prima i. Questo diventa un problema se uno viene utilizzato per inizializzare l'altro, come in

struct A {
  int i;
  int j;
  A(): j (0), i (this->j) { }
};

Quando guardi solo il costruttore, questo sembra sicuro. Ma in realtà, jnon è stato ancora inizializzato nel punto in cui viene utilizzato per inizializzare i, quindi il codice non funzionerà come previsto. Da qui l'avvertimento.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.