В чем смысл 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.

person Peeter Joot    schedule 01.12.2009    source источник
comment
Здесь есть несколько хороших ответов, но вкратце на случай, если это кого-то заинтересует: в g ++ есть флаг, рассматривающий это как полномасштабную ошибку: -Werror=reorder   -  person Max Barraclough    schedule 17.11.2018


Ответы (5)


Учитывать:

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

Теперь i инициализируется некоторым неизвестным значением, а не нулем.

В качестве альтернативы инициализация i может иметь некоторые побочные эффекты, для которых важен порядок. Например.

A(int n) : j(n++), i(n++) { }
person int3    schedule 01.12.2009
comment
Это действительно должен быть пример в документации. - person Ben S; 01.12.2009
comment
благодаря. Поскольку большинство наших типов являются типами POD с простыми инициализаторами, мне этого не приходило в голову. Ваш пример намного лучше, чем ручной пример g ++. - person Peeter Joot; 01.12.2009
comment
Пример в этом ответе ужасно сбивает с толку. Когда я проверил это, я обнаружил, что i был последовательно инициализирован как 0, из-за чего кажется, что нет ничего ошибочного. Но если вы используете что-то вроде 100, вы увидите, что j это 100, а i это 0. Я знаю, что это, вероятно, связано с компилятором, но я использую любой g ++ по умолчанию, который я получаю с Ubuntu, поэтому это должно быть обычным явлением. - person Mike S; 22.09.2015
comment
@Mike это потому, что ваш компилятор (gcc) инициализирует неинициализированные переменные значением 0, но это не то, от чего вы должны зависеть; i, равный 0, является побочным эффектом неизвестного значения 0 для неинициализированных переменных. - person ethanwu10; 15.02.2016
comment
Код взят из справочной страницы gcc (linux.die.net/man/1/gcc) но не цитируется! - person codingdave; 04.08.2016
comment
@codingdave Вы уверены, что порядок идет на страницу руководства - ›это, а не наоборот? - person Yakk - Adam Nevraumont; 21.12.2016
comment
@Yakk Заказ был на странице руководства- ›ТАК, ответь. Вот архив справочной страницы 2007 года, в которой явно указан этот пример. Комментарий от Ben S - это забавный пример того, как кто-то предполагает, что что-то существует, даже не проверив, что оно уже существует. http://web.archive.org/web/20070712184121/http://linux.die.net/man/1/gcc - person KymikoLoco; 30.01.2017
comment
Вопрос касается кода с man-страницы gcc и указывает на то, что он на самом деле не демонстрирует, почему полезен -Wreorder. Я придумал лучший пример, который лучше иллюстрирует проблему. Этого кода все еще нет в документации gcc. - person int3; 31.01.2017
comment
@KymikoLoco Это просто неправильно. Пример на странице руководства взят из OP (где i инициализируется значением 1). Здесь i инициализируется значением j, что на самом деле демонстрирует проблему. - person jazzpi; 04.02.2017
comment
@jazzpi Хех, ты прав. Я проверил и перепроверил и не заметил этого единственного изменения символа, поскольку уверен, что codingdave тоже пропустил. В любом случае codingdave неверен, он не цитируется, потому что его нет на страницах руководства. Бен С. на самом деле прав, это должен быть пример (или пример). И я ошибаюсь, потому что этого ТАК-ответа нет на страницах руководства, в прошлом ИЛИ в настоящем. - person KymikoLoco; 06.02.2017
comment
Почему i инициализируется неизвестным значением во фрагменте? - person Nubcake; 17.08.2017

Проблема в том, что кто-то может увидеть список инициализаторов членов в конструкторе и подумать, что они выполняются в этом порядке (сначала j, затем i). Это не так, они выполняются в том порядке, в котором члены определены в классе.

Допустим, вы написали A(): j(0), i(j) {}. Кто-то может прочитать это и подумать, что i заканчивается значением 0. Это не так, потому что вы инициализировали его с помощью j, который содержит мусор, потому что он сам не был инициализирован.

Предупреждение напоминает вам написать A(): i(j), j(0) {}, что, надеюсь, выглядит более подозрительно.

person Steve Jessop    schedule 01.12.2009
comment
Выглядит / пахнет действительно подозрительно! :) Определенно запах кода :) Спасибо за четкое и точное объяснение. :) - person Will; 15.08.2017
comment
... напоминает вам написать A (): i (j), j (0) {} ... Я предлагаю, чтобы он напоминал вам изменить порядок членов класса в этом конкретном случае. - person 2.718; 22.02.2018

В других ответах приводятся хорошие примеры, оправдывающие вариант предупреждения. Я подумал, что приведу некоторый исторический контекст. Создатель C ++ Бьярн Страуструп объясняет в своей книге Программирование на C ++ язык (3-е издание, стр. 259):

Конструкторы членов вызываются перед выполнением тела собственного конструктора содержащего класса. Конструкторы вызываются в том порядке, в котором они объявлены в классе, а не в том порядке, в котором они появляются в списке инициализаторов. Чтобы избежать путаницы, лучше всего указывать инициализаторы в порядке объявления. Деструкторы членов вызываются в порядке, обратном построению.

person gkb0986    schedule 16.02.2013

Это может вас укусить, если ваши инициализаторы имеют побочные эффекты. Учитывать:

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 относятся к определенному пользователем типу с конструктором, этот конструктор также может иметь побочные эффекты с тем же неочевидным результатом.

Он также может проявляться, когда инициализатор для одного члена ссылается на другой член.

person Pavel Minaev    schedule 01.12.2009

Предупреждение существует, потому что если вы просто прочитаете конструктор, похоже, что j инициализируется до i. Это становится проблемой, если один используется для инициализации другого, как в

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

Когда вы просто смотрите на конструктор, это выглядит безопасным. Но на самом деле j еще не был инициализирован в точке, где он используется для инициализации i, и поэтому код не будет работать должным образом. Отсюда и предупреждение.

person jalf    schedule 01.12.2009