What's the point of g++ -Wreorder?

C++G++Compiler Warnings

C++ Problem Overview


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.

C++ Solutions


Solution 1 - C++

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++) { }

Solution 2 - C++

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.

Solution 3 - C++

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.

Solution 4 - C++

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.

Solution 5 - C++

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPeeter JootView Question on Stackoverflow
Solution 1 - C++int3View Answer on Stackoverflow
Solution 2 - C++Steve JessopView Answer on Stackoverflow
Solution 3 - C++gkb0986View Answer on Stackoverflow
Solution 4 - C++Pavel MinaevView Answer on Stackoverflow
Solution 5 - C++jalfView Answer on Stackoverflow