Вопрос

Опция 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, и поэтому код не будет работать так, как ожидалось.Отсюда и предупреждение.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top