Какой смысл g++ -Wreorder?
В опции g++ -Wall есть -Wreorder. Эта опция описана ниже. Для меня это не очевидно, почему кому-то это нужно (особенно, чтобы включить это по умолчанию в -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.
Ответы
Ответ 1
Рассмотрим:
struct A {
int i;
int j;
A() : j(0), i(j) { }
};
Теперь i
инициализируется некоторым неизвестным значением, а не нулем.
Альтернативно, инициализация i
может иметь некоторые побочные эффекты, для которых порядок важен. Например.
A(int n) : j(n++), i(n++) { }
Ответ 2
Проблема в том, что кто-то может увидеть список инициализаторов-членов в конструкторе и подумать, что они выполнены в этом порядке (сначала, а затем i). Их нет, они выполняются в том порядке, в котором члены определены в классе.
Предположим, вы написали A(): j(0), i(j) {}
. Кто-то может это прочитать и подумать, что я заканчиваю значением 0. Это не так, потому что вы инициализировали его с помощью j, который содержит мусор, поскольку он сам не инициализирован.
Предупреждение напоминает вам написать A(): i(j), j(0) {}
, который, надеюсь, выглядит намного более подозрительным.
Ответ 3
Это может укусить вас, если ваши инициализаторы имеют побочные эффекты. Рассмотрим:
int foo() {
puts("foo");
return 1;
}
int bar() {
puts("bar");
return 2;
}
struct baz {
int x, y;
baz() : y(foo()), x(bar()) {}
};
Вышеприведенная версия будет печатать "bar", затем "foo", хотя интуитивно предположить, что порядок указан в списке инициализаторов.
В качестве альтернативы, если x
и y
имеют определенный пользовательский тип с конструктором, этот конструктор может также иметь побочные эффекты с тем же неочевидным результатом.
Он также может проявляться, когда инициализатор для одного члена ссылается на другой член.
Ответ 4
Другие ответы предоставили несколько хороших примеров, которые оправдывают вариант предупреждения. Я думал, что предоставил бы какой-то исторический контекст. Создатель С++, Bjarne Stroustrup, объясняет в своей книге язык программирования С++ (3-е издание, стр. 259):
Конструкторы членов вызывают до того, как выполняется тело собственного конструктора содержащего класса. Конструкторы вызываются в том порядке, в котором они объявлены в классе, а не в порядке, в котором они отображаются в списке инициализаторов. Чтобы избежать путаницы, лучше указать инициализаторы в порядке объявления. Деструкторы-члены вызываются в обратном порядке построения.
Ответ 5
Предупреждение существует, потому что, если вы просто прочитали конструктор, похоже, что j
инициализируется до i
. Это становится проблемой, если вы используете для инициализации другого, как в
struct A {
int i;
int j;
A(): j (0), i (this->j) { }
};
Когда вы просто смотрите на конструктор, это выглядит безопасно. Но на самом деле j
еще не инициализирован в точке, где он используется для инициализации i
, поэтому код не будет работать должным образом. Отсюда предупреждение.