В чем смысл g ++ -Wreorder?
-
11-09-2019 - |
Вопрос
Опция g++ - Wall включает в себя -Wreorder.Что делает эта опция, описано ниже.Для меня не очевидно, почему кого-то это волнует (особенно настолько, чтобы включить это по умолчанию в стене).
-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.
Решение
Рассмотреть:
struct A {
int i;
int j;
A() : j(0), i(j) { }
};
Сейчас i
инициализируется некоторым неизвестным значением, а не нулем.
В качестве альтернативы, инициализация i
могут иметь некоторые побочные эффекты, для которых важен порядок.Например.
A(int n) : j(n++), i(n++) { }
Другие советы
Проблема в том, что кто-то может увидеть список инициализаторов элементов в конструкторе и подумать, что они выполняются в таком порядке (сначала j, затем i).Это не так, они выполняются в том порядке, в котором члены определены в классе.
Предположим, вы написали A(): j(0), i(j) {}
.Кто-то может прочитать это и подумать, что i в конечном итоге получает значение 0.Это не так, потому что вы инициализировали его с помощью j, который содержит мусор, потому что сам он не был инициализирован.
Предупреждение напоминает вам о необходимости написать A(): i(j), j(0) {}
, что, надеюсь, выглядит намного более подозрительно.
В других ответах приведено несколько хороших примеров, которые оправдывают выбор предупреждения.Я подумал, что приведу некоторый исторический контекст.Создатель C ++ Бьярне Страуструп объясняет в своей книге Язык программирования C ++ (3-е издание, стр. 259):
Конструкторы элементов вызываются перед выполнением тела собственного конструктора содержащего класса.Конструкторы вызываются в том порядке, в котором они объявлены в классе, а не в том порядке, в котором они отображаются в списке инициализаторов.Чтобы избежать путаницы, лучше всего указывать инициализаторы в порядке объявления.Деструкторы элементов вызываются в порядке, обратном построению.
Это может вас огорчить, если у ваших инициализаторов есть побочные эффекты.Рассмотреть:
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
имеют некоторый пользовательский тип с конструктором, этот конструктор также может иметь побочные эффекты с тем же неочевидным результатом.
Это также может проявляться, когда инициализатор для одного элемента ссылается на другой элемент.
Предупреждение существует, потому что, если вы просто прочитаете конструктор, это выглядит как j
инициализируется ли перед i
.Это становится проблемой, если один используется для инициализации другого, как в
struct A {
int i;
int j;
A(): j (0), i (this->j) { }
};
Когда вы просто смотрите на конструктор, это выглядит в безопасности.Но на самом деле, j
еще не был инициализирован в том месте, где он используется для инициализации i
, и поэтому код не будет работать так, как ожидалось.Отсюда и предупреждение.