c++ - ¿Cuál es el punto de g++-Wreorder?
compiler-warnings (5)
La opción g ++ -Wall incluye -Wreorder. Lo que hace esta opción se describe a continuación. No es obvio para mí por qué a alguien le importaría (especialmente lo suficiente para activar esto por defecto en -Wall).
-Wreorder (C++ only) Warn when the order of member initializers given in the code does not match the order in which they must be executed. For instance: struct A { int i; int j; A(): j (0), i (1) { } }; The compiler will rearrange the member initializers for i and j to match the declaration order of the members, emit-ting a warning to that effect. This warning is enabled by -Wall.
Considerar:
struct A {
int i;
int j;
A() : j(0), i(j) { }
};
Ahora i
se inicializa con un valor desconocido, no con cero.
Alternativamente, la inicialización de i
puede tener algunos efectos secundarios para los cuales el orden es importante. P.ej
A(int n) : j(n++), i(n++) { }
El problema es que alguien podría ver la lista de iniciadores de miembros en el constructor y pensar que se ejecutan en ese orden (primero, luego yo). No lo son, se ejecutan en el orden en que los miembros se definen en la clase.
Supongamos que escribió A(): j(0), i(j) {}
. Alguien podría leer eso, y pensar que termino con el valor 0. No, porque lo inicializó con j, que contiene basura porque no se ha inicializado.
La advertencia te recuerda escribir A(): i(j), j(0) {}
, que con suerte se ve mucho más sospechoso.
Esto puede morderte si tus inicializadores tienen efectos secundarios. Considerar:
int foo() {
puts("foo");
return 1;
}
int bar() {
puts("bar");
return 2;
}
struct baz {
int x, y;
baz() : y(foo()), x(bar()) {}
};
Lo anterior imprimirá "bar" y luego "foo", aunque intuitivamente uno supondría que el orden es como está escrito en la lista de inicializadores.
Alternativamente, si y
son de algún tipo definido por el usuario con un constructor, ese constructor también puede tener efectos secundarios, con el mismo resultado no obvio.
También se puede manifestar cuando el inicializador de un miembro hace referencia a otro miembro.
La advertencia existe porque si acabas de leer el constructor, parece que j
se está inicializando antes de i
. Esto se convierte en un problema si uno se usa para inicializar el otro, como en
struct A {
int i;
int j;
A(): j (0), i (this->j) { }
};
Cuando miras al constructor, parece seguro. Pero en realidad, j
aún no se ha inicializado en el punto en que se usa para inicializar i
, por lo que el código no funcionará como se esperaba. De ahí la advertencia.
Otras respuestas han proporcionado algunos buenos ejemplos que justifican la opción de una advertencia. Pensé que proporcionaría un contexto histórico. El creador de C ++, Bjarne Stroustrup, explica en su libro The C ++ programming language (3rd edition, Page 259):
Se llama a los constructores de los miembros antes de que se ejecute el cuerpo del propio constructor de la clase contenedora. Los constructores se llaman en el orden en que se declaran en la clase en lugar del orden en el que aparecen en la lista de inicializadores. Para evitar confusiones, es mejor especificar los inicializadores en orden de declaración. Los destructores miembros se llaman en el orden inverso de construcción.