web-dev-qa-db-fra.com

Quel est l'intérêt de g ++ -Wreorder?

L'option g ++ -Wall inclut -Wreorder. La fonction de cette option est décrite ci-dessous. Il n'est pas évident pour moi pourquoi quelqu'un s'en soucierait (surtout assez pour activer cela par défaut dans -Wall).

 - Wreorder (C++ uniquement) 
 Avertir lorsque l'ordre des initialiseurs de membre donné dans le code ne correspond pas à 
 L'ordre dans lequel ils doivent être exécutés. Par exemple: 
 
 Struct A {
 Int i; 
 Int j; 
 A (): j (0), i (1) {} 
}; 
 
 Le compilateur réorganisera les initialiseurs de membre pour i et j pour 
 Correspondre à l'ordre de déclaration des membres, émettant un avertissement à cet 
 effet. Cet avertissement est activé par -Wall. 
135
Peeter Joot

Considérer:

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

Maintenant, i est initialisé à une valeur inconnue, pas à zéro.

Alternativement, l'initialisation de i peut avoir des effets secondaires pour lesquels l'ordre est important. Par exemple.

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

Le problème est que quelqu'un peut voir la liste des initialiseurs de membres dans le constructeur et penser qu'ils sont exécutés dans cet ordre (j d'abord, puis i). Ils ne le sont pas, ils sont exécutés dans l'ordre dans lequel les membres sont définis dans la classe.

Supposons que vous ayez écrit A(): j(0), i(j) {}. Quelqu'un pourrait lire cela et penser que i se retrouve avec la valeur 0. Ce n'est pas le cas, parce que vous l'avez initialisé avec j, qui contient du courrier indésirable parce qu'il n'a pas lui-même été initialisé.

L'avertissement vous rappelle d'écrire A(): i(j), j(0) {}, qui, espérons-le, semble beaucoup plus louche.

36
Steve Jessop

D'autres réponses ont fourni de bons exemples qui justifient l'option d'un avertissement. Je pensais fournir un contexte historique. Le créateur de C++, Bjarne Stroustrup, explique dans son livre Le langage de programmation C++ (3e édition, page 259):

Les constructeurs des membres sont appelés avant l'exécution du corps du propre constructeur de la classe conteneur. Les constructeurs sont appelés dans l'ordre dans lequel ils sont déclarés dans la classe plutôt que dans l'ordre dans lequel ils apparaissent dans la liste d'initialisation. Pour éviter toute confusion, il est préférable de spécifier les initialiseurs dans l'ordre de déclaration. Les destructeurs de membres sont appelés dans l'ordre inverse de la construction.

14
gkb0986

Cela peut vous mordre si vos initialiseurs ont des effets secondaires. Considérer:

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

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

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

Ce qui précède affichera "bar" puis "foo", même si intuitivement on pourrait supposer que l'ordre est tel qu'écrit dans la liste d'initialisation.

Alternativement, si x et y sont d'un type défini par l'utilisateur avec un constructeur, ce constructeur peut également avoir des effets secondaires, avec le même résultat non évident.

Il peut également se manifester lorsque l'initialiseur d'un membre fait référence à un autre membre.

9
Pavel Minaev

L'avertissement existe parce que si vous venez de lire le constructeur, il semble que j soit initialisé avant i. Cela devient un problème si l'un est utilisé pour initialiser l'autre, comme dans

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

Quand vous regardez juste le constructeur, cela regarde sûr. Mais en réalité, j n'a pas encore été initialisé au point où il est utilisé pour initialiser i, et donc le code ne fonctionnera pas comme prévu. D'où l'avertissement.

7
jalf