简体   繁体   中英

Is it allowed to call a non-static member function in a default member initializer?

Consider this class:

#include <iostream>

struct foo {
    int a = 42;
    int b = bar();
    int bar() { return a; }
};

int main(){
    foo f;
    std::cout << f.a << " " << f.b;
}

It prints the expected 42 42 . Is it allowed by the standard to call a member function in a default member initializer?

The following I would expect to be undefined:

struct broken {
    int a = bar();
    int b = 42;       
    int bar() { return b; }
};

Unfortunately it does compile without warnings .

As you found, this is legal, but brittle and not recommended. When you specify default initializers for class members those are just syntactic sugar for use this value in the class member initializer list. So, if we look at when we can call a member function we find [class.cdtor]/1 and [class.cdtor]/4 which states:

1) For an object with a non-trivial constructor, referring to any non-static member or base class of the object before the constructor begins execution results in undefined behavior . For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor finishes execution results in undefined behavior.

4) Member functions, including virtual functions ([class.virtual]), can be called during construction or destruction ([class.base.init]).[...]

emphasis mine

Since the constructor has begun executing, and we are allowed to call member functions, we are not in UB land.

The next thing we have to consider is construction order, since the members depend on that. That information is in [class.base.init]/13

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, the members are constructed in the order they are declared in the class which means in your first example you refer to a after it has been initialized so you are not in UB land.

In your second example you are referring to an object that has not yet been initialized and reading an uninitialized object's value is undefined behavior.

The standard says here :

Member functions (including virtual member functions, [class.virtual]) can be called for an object under construction. Similarly, an object under construction can be the operand of the typeid operator ([expr.typeid]) or of a dynamic_cast ([expr.dynamic.cast]). However, if these operations are performed in a ctor-initializer (or in a function called directly or indirectly from a ctor-initializer) before all the mem-initializers for base classes have completed, the program has undefined behavior.

Since you have no base classes, the member functions can be called for broken even if it is under construction. Your a will be initialized with indeterminate value.

I was a bit premature. As seen in the other answer, there is the problem, that the function reads from an unitialized value which is undefined behavior. So not the call of this function in itself, but rather what it does is UB.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM