Can a member variable access the value of a previously defined member variable in the same initialization list (C++)?

3434 views c++
-5

For example:

Cls::Cls(int *p) : m1_(1), m2_(m1), m3_(p), m4_(m3) {}

where Cls::m1_ and Cls::m2_ are of type int and m3 and m4 are of type int*.

Is this legal? If it is legal, does it do what one would expect?

answered question

Yes if the member was defined in the class BEFORE the member you are currently trying to initialize. Regardless of the order used in the initializer list, members will be initialized in the order of definition.

@user4581301 why would it be dependent on the order in which it is defined in the class? That seems like an absurd language specification

@ubadub Because C++ is an absurd language and the order the variables are declared in the class is the actual order they are initialized in, not the way you do it in the list

@NathanOliver It's not absurd that members are initialized in declaration order---that is necessary to ensure that they're destroyed in the right order. What's absurd is that it's not a compilation error when you get the order wrong in the ctor-initializer.

All members are always initialized before entering the body of the constructor, so this rule forces the same initialization order as when you do not use an intitializer list.

So I'm guessing the "best practice" is not to do this? Follow up question, are all member variables in an initializer list initialized before the main body of the constructor is executed? @user4581301

Everything that can be initialised is initialised before the body of the constructor is entered. So normally, the body of the constructor should be empty.

All of them, even if the initialization is to do nothing.

@Nathan Sarcasm? Irony?

@NeilButterworth Kind of both.

3 Answers

11

Yes, it's legal.

class C
{
public:
    C() : m1_(1), m2_(m1_ + 1), m3_(m2_ + 2), m4_(m3_ + 3) {} 

    int m1_ = 0;
    int m2_ = 0;
    int m3_ = 0;
    int m4_ = 0;
};

int main()
{
    C c;
    std::cout << c.m1_ << std::endl;
    std::cout << c.m2_ << std::endl;
    std::cout << c.m3_ << std::endl;
    std::cout << c.m4_ << std::endl;
}

The output:

1
2
4
7

posted this
0

As pointed out in the comments, non-static data members are initialized in declaration order. In order to prevent confusion, you should always write the member initializers in declaration order. In the case presented in the question, I will therefore assume that the order in which the members were declared is:

int m1_;
int m2_;
int* m3_;
int* m4_;

Assuming that is the case, the code is well-defined and does what you expect it to do. This is guaranteed by [intro.execution]/12.3, which guarantees that each member initialization is a full-expression, and [class.base.init]/13.3, which guarantees that these initializations occur in declaration order.

posted this
8

This is legal-ish. If you have

struct foo
{
    int a;
    int b;
    int c;
    foo() : a(1), b(a + 1), c(b + 1) {}
};

Then this is fine. The reason is a is declared in the class before b and b is declared before c. All class members are initialized in the order they are declared in the class, so you could even have

struct foo
{
    int a;
    int b;
    int c;
    foo() : c(b + 1), b(a + 1), a(1) {}
};

and you would still be okay. If you had

struct foo
{
    int b;
    int c;
    int a;
    foo() : a(1), b(a + 1), c(b + 1) {}
};

on the other hand, both b and c would be initialized with an indeterminate value and using them would be undefined behavior.

posted this

Have an answer?

JD

Please login first before posting an answer.