Quel est le but de g ++ -Wreorder?

L’option g ++ -Wall inclut l’option -Wreorder. Ce que cette option fait est décrit ci-dessous. Je ne vois pas pourquoi quelqu’un s’en préoccuperait (en particulier pour activer cette option par défaut dans -Wall).

 -Wreorder (C ++ uniquement) Avertit lorsque l'ordre d'initialisation des membres 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 membres pour i et j afin qu'ils correspondent à l'ordre de déclaration des membres, émettant un avertissement à cet effet.  Cet avertissement est activé par -Wall. 

Considérer:

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

Maintenant, i initialisé à une valeur inconnue, et non à 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++) { } 

Le problème est que quelqu’un pourrait 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 je finis par avoir la valeur 0. Ce n’est pas le cas, car vous l’avez initialisé avec j, qui contient des données indésirables car il n’a pas été initialisé.

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

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 que le corps du constructeur de la classe contenant soit exécuté. 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 membres sont appelés dans l’ordre inverse de la construction.

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 peut supposer que l’ordre est é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 aussi 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.

L’avertissement existe car si vous lisez simplement le constructeur, il semble que j soit initialisé avant i . Cela devient un problème si l’on utilise pour initialiser l’autre, comme dans

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

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