Are functions calls in a constructor's initializer-list sequenced?

2.5k Views Asked by At

Consider:

int f () {
    static int i = 0;
    return i++;
}

struct Test {
    int a, b;
    Test () : a(f()), b(f()) {}
};

Test t;

I know that a is initialized before b due to the order of their declaration in the struct.

I also know that the two calls to f in g(f(), f()) are unsequenced.

So I am wondering if it is guaranteed that t.a == 0 and t.b == 1?

2

There are 2 best solutions below

2
NathanOliver On BEST ANSWER

So I am wondering if it is guaranteed that t.a == 0 and t.b == 1?

This will always be true so long as a comes before b in the class declaration and nothing else calls f() between the initialization of a and b. Class members are initialized in the order they are declared in the class. [class.base.init]/11:

In a non-delegating constructor, initialization proceeds in the following order: [...]

  • Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).

So since a comes before b then when the constructor initializes a it will call f() the first time and then it will call it a second time when it initializes b.

We also know there is a sequence point between member initializer because [class.base.init]/7:

[...]The initialization performed by each mem-initializer constitutes a full-expression. Any expression in a mem-initializer is evaluated as part of the full-expression that performs the initialization.

tells us each initializer is a full expression and each full expression is sequenced: [intro.execution]/14

Every value computation and side effect associated with a full-expression is sequenced before every value computation and side effect associated with the next full-expression to be evaluated.

0
R Sahu On

I know that a is initialized before b due to the order of their declaration in the struct.

That's true.

My interpretation of that constraint is that a cannot be initialized before b unless the evaluation of the initializer expression is complete before b is initialized.

I don't see anything in the standard that speaks of sequencing the evaluation of the expressions used to initialize non-static members. However, I see the following example in the C++11 Standard (12.6.2/12):

Names in the expression-list or braced-init-list of a mem-initializer are evaluated in the scope of the constructor for which the mem-initializer is specified. [ Example:

class X {
  int a;
  int b;
  int i;
  int j;
  public:
  const int& r;
  X(int i): r(a), b(i), i(i), j(this->i) { }
};

That won't be valid unless the evaluation of this->i is sequenced after i is initialized.