181

The g++ -Wall option includes -Wreorder. What this option does is described below. It is not obvious to me why somebody would care (especially enough to turn this on by default in -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.
Peeter Joot
  • 7,848
  • 7
  • 48
  • 82
  • 4
    Some good answers here, but a brief aside in case it's of interest to anyone: g++ has a flag to treat this as a full-blown error: `-Werror=reorder` – Max Barraclough Nov 17 '18 at 13:23

6 Answers6

295

Consider:

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

Now i is initialized to some unknown value, not zero.

Alternatively, the initialization of i may have some side effects for which the order is important. E.g.

A(int n) : j(n++), i(n++) { }
int3
  • 12,861
  • 8
  • 51
  • 80
  • 97
    This should really be the example in the documentation. – Ben S Dec 01 '09 at 18:42
  • 3
    thanks. With most of our types being POD types with simple initializers this didn't occur to me. Your example is much better than the g++ manual example. – Peeter Joot Dec 01 '09 at 19:22
  • The example in this is answer is terribly confounding. When I tested this I found that `i` was **consistently initialized** to `0`, which makes it seem like nothing is erroneous. But if you use something like `100` then you see that `j` is `100` and `i` is `0`. I know it's probably due to the compiler but I'm using whatever default g++ I get with Ubuntu so it should be a common occurrence. – Mike S Sep 22 '15 at 18:52
  • 5
    @Mike this is because your compiler (gcc) initializes uninitialized variables to 0, but this isn't something that you should depend on; i being 0 is just a side-effect of the unknown value for uninitialized variables is 0. – ethanwu10 Feb 15 '16 at 20:40
  • 1
    The code origins from the manpage of gcc (http://linux.die.net/man/1/gcc) but is not cited! – codingdave Aug 04 '16 at 08:07
  • @codingdave Are you sure the order goes man page -> this, and not the other way around? – Yakk - Adam Nevraumont Dec 21 '16 at 15:10
  • 2
    @Yakk The order was man page-> SO answer. Here is an archive of the man page from 2007 that lists this example explicitly. The upvoted comment from Ben S is a hilarious example of someone suggesting that something exist without even checking that it does already. http://web.archive.org/web/20070712184121/http://linux.die.net/man/1/gcc – KymikoLoco Jan 30 '17 at 19:29
  • The question asks about the code from the manpage of gcc and points out that it doesn't really demonstrate why -Wreorder is useful. I came up with a better example that better illustrates the problem. This code is still *not* in the gcc docs. – int3 Jan 31 '17 at 01:53
  • 3
    @KymikoLoco That is just plain wrong. The example in the man page is the one from the OP (where `i` is initialized to `1`). Here, `i` is initialized to `j`, which actually demonstrates a problem. – jazzpi Feb 04 '17 at 16:57
  • 2
    @jazzpi Heh, you're right. I checked and rechecked and didn't catch that one character change, as I'm sure codingdave missed as well. In any case, codingdave is incorrect, it's not cited because it's not in the man pages. Ben S is actually correct, it should be an example (or *the* example). And I'm wrong because this SO answer is not in the man pages, in the past OR present. – KymikoLoco Feb 06 '17 at 18:44
  • 2
    Why does `i` get initialised to an unknown value in the snippet? – Nubcake Aug 17 '17 at 18:23
  • @Nubcake because the order of initialization is the order of the variables in the class, not the order of the initializers in the constructor. At the time `i` is initialized, `j` hasn't been initialized yet. – Mark Ransom Jun 26 '23 at 14:56
56

The problem is that somebody might see the list of member initialisers in the constructor, and think that they're executed in that order (j first, then i). They are not, they are executed in the order the members are defined in the class.

Suppose you wrote A(): j(0), i(j) {}. Somebody might read that, and think that i ends up with the value 0. It doesn't, because you initialised it with j, which contains junk because it has not itself been initialised.

The warning reminds you to write A(): i(j), j(0) {}, which hopefully looks a lot more fishy.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • Looks/smells fishy indeed! :) Definitely code smell :) Thanks for your clear explanation that is right to the point. :) – Will Aug 15 '17 at 07:58
  • 1
    "... reminds you to write A(): i(j), j(0) {} ..." I suggest that it reminds you to reorder the class members in this particular case. – 2.718 Feb 22 '18 at 04:15
29

Other answers have provided some good examples that justify the option for a warning. I thought I'd provide some historical context. The creator of C++, Bjarne Stroustrup, explains in his book The C++ programming language (3rd edition, Page 259):

The members’ constructors are called before the body of the containing class’ own constructor is executed. The constructors are called in the order in which they are declared in the class rather than the order in which they appear in the initializer list. To avoid confusion, it is best to specify the initializers in declaration order. The member destructors are called in the reverse order of construction.

gkb0986
  • 3,099
  • 1
  • 24
  • 22
13

This can bite you if your initializers have side effects. Consider:

int foo() {
    puts("foo");
    return 1;
}

int bar() {
    puts("bar");
    return 2;
}

struct baz {
    int x, y;
    baz() : y(foo()), x(bar()) {}
};

The above will print "bar" then "foo", even though intuitively one would assume that order is as written in the initializer list.

Alternatively, if x and y are of some user-defined type with a constructor, that constructor may also have side effects, with the same non-obvious result.

It can also manifest itself when initializer for one member references another member.

Pavel Minaev
  • 99,783
  • 25
  • 219
  • 289
8

The warning exists because if you just read the constructor, it looks like j is getting initialized before i. This becomes a problem if one is used to initialize the other, as in

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

When you just look at the constructor, this looks safe. But in reality, j has not yet been initialized at the point where it is used to initialize i, and so the code won't work as expected. Hence the warning.

jalf
  • 243,077
  • 51
  • 345
  • 550
0

Lots of good answers already, but there's one thing missing - why is the initialization order the way it is?

The order of initialization of class members is not the order that initializers are listed in the constructor, it's the order they're declared in the class itself. The reason why is a bit subtle.

C++ guarantees that the order of destruction will be the opposite of the order of construction for member variables. Since a class can have multiple constructors with different initializer lists, the destructor would need to know that order which would be a big bookkeeping problem and introduce unnecessary overhead. The solution is to force every constructor to use the same order of initialization for members so that the destruction order can be fixed as well.

Since you can't use the initializer order, the only choice is to use the declaration order.

While it is perfectly legal for C++ to initialize the variables in a different order than the initializer list, it's a non-intuitive source of possible bugs. The warning lets you know to look for those bugs, or to reorder your initializer list to reflect reality.

starball
  • 20,030
  • 7
  • 43
  • 238
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • I was asked about initializer order once in an interview, and I got it wrong. I asked for an explanation from the interviewer, and learned something that day. Time to pass it along. – Mark Ransom Jun 26 '23 at 16:52