Skip to main content

C++ initializer list order in a constructor matters?

This is a computer related article, and about THE hardest language, C++. So, I think the audience is again limited.

gcc's -Wall option sometimes gives a warning as the order of initializer list in the constructor is wrong. For example,

% g++ -Wall initializer_list.cc
initializer_list.cc: In constructor 'InitializerListTest::InitializerListTest()':
initializer_list.cc:30: warning: 'InitializerListTest::d_j' will be initialized after
initializer_list.cc:29: warning: 'int InitializerListTest::d_i'
initializer_list.cc:7: warning: when initialized here.

This warning said that the order of initializer list in the constructor and the member variables declaration order are different. The following code is such an example.

---
#include

class InitializerListTest {
public:
/// constructor
InitializerListTest() :
d_j(123), // j = 123;
d_i(d_j - 1) // i = j - 1;
{
// empty
}
/// print out
void print()
{
std::cout << "i = " << d_i << ", j = " << d_j << std::endl;
}

private:
/// i, j
int d_i;
int d_j;
};

int main()
{
InitializerListTest ilt;
ilt.print();
return 0;
}
---

This warning pointed out that the member initialization order does not related with the order of initializer list in C++. In the C++ book 3rd edition by Stroustrup, Section 10.4.6 Class Objects as Members describes this as "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." This is the meaning of the warning. In the above sample code, the initializer list is

d_j(123),
d_i(d_j - 1).

This looks like the following code,

d_j = 123;
d_i = (d_j - 1);.

But, actually, it is

d_i = (d_j - 1);
d_j = 123;.

Therefore, the value of d_i is undefined. In my environment, the execution result is

i = 32766, j = 123.


Since this is a source of the confusion, I agree the C++ book and gcc warnings. But, the real problem comes from the initialization depends on other member initialization. In such case, I avoid to initialize the member in the initialization list, rather do in the constructor body. It would be more accurate that if the warning detects the dependency between the initializer list.

The answer of the title of this article, "C++ initializer list order in a constructor matters?", would be Yes. By the way, Lisp has let and let* for the block local variable initialization. We can initialize the let function's element in Lisp, and let* is the sequential version, which defines the order of initialization. C++'s initializer list is an implicit version of the let*.

I think it is a bit unnatural in C++ since C++ has only implicit let*, which is based on sequential model. C++'s design is usually tend to execution time optimization. But this definition lead us to make it hard to understand and to be potentially slower. (Or is it a nature of C++?)

Comments

Dividend Lover said…
good explaination thanks

Popular posts from this blog

Why A^{T}A is invertible? (2) Linear Algebra

Why A^{T}A has the inverse Let me explain why A^{T}A has the inverse, if the columns of A are independent. First, if a matrix is n by n, and all the columns are independent, then this is a square full rank matrix. Therefore, there is the inverse. So, the problem is when A is a m by n, rectangle matrix.  Strang's explanation is based on null space. Null space and column space are the fundamental of the linear algebra. This explanation is simple and clear. However, when I was a University student, I did not recall the explanation of the null space in my linear algebra class. Maybe I was careless. I regret that... Explanation based on null space This explanation is based on Strang's book. Column space and null space are the main characters. Let's start with this explanation. Assume  x  where x is in the null space of A .  The matrices ( A^{T} A ) and A share the null space as the following: This means, if x is in the null space of A , x is also in the null spa

Gauss's quote for positive, negative, and imaginary number

Recently I watched the following great videos about imaginary numbers by Welch Labs. https://youtu.be/T647CGsuOVU?list=PLiaHhY2iBX9g6KIvZ_703G3KJXapKkNaF I like this article about naming of math by Kalid Azad. https://betterexplained.com/articles/learning-tip-idea-name/ Both articles mentioned about Gauss, who suggested to use other names of positive, negative, and imaginary numbers. Gauss wrote these names are wrong and that is one of the reason people didn't get why negative times negative is positive, or, pure positive imaginary times pure positive imaginary is negative real number. I made a few videos about explaining why -1 * -1 = +1, too. Explanation: why -1 * -1 = +1 by pattern https://youtu.be/uD7JRdAzKP8 Explanation: why -1 * -1 = +1 by climbing a mountain https://youtu.be/uD7JRdAzKP8 But actually Gauss's insight is much powerful. The original is in the Gauß, Werke, Bd. 2, S. 178 . Hätte man +1, -1, √-1) nicht positiv, negative, imaginäre (oder gar um

Why parallelogram area is |ad-bc|?

Here is my question. The area of parallelogram is the difference of these two rectangles (red rectangle - blue rectangle). This is not intuitive for me. If you also think it is not so intuitive, you might interested in my slides. I try to explain this for hight school students. Slides:  A bit intuitive (for me) explanation of area of parallelogram  (to my site, external link) .