是否允许在默认成员初始值设定项中调用非静态成员函数?

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

考虑这个 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;
}

它打印出预期的 42 42。标准是否允许在默认成员初始值设定项中调用成员函数?

以下我认为是未定义的:

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

不幸的是 compile without warnings

标准说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.

因为你没有基类,所以broken即使在构造中也可以调用成员函数。 您的 a 将被初始化为不确定的值。

我有点早熟。正如在另一个答案中看到的那样,存在一个问题,即函数从未定义行为的未初始化值中读取。所以不是这个函数本身的调用,而是它所做的是 UB。

如您所见,这是合法的,但很脆弱,不推荐使用。当您为 class 成员指定默认初始化程序时,这些只是在 class 成员初始化程序列表中使用此值的语法糖。所以,如果我们看看什么时候可以调用成员函数,我们会发现 [class.cdtor]/1 and [class.cdtor]/4 表示:

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]).[...]

强调我的

既然构造函数已经开始执行,并且允许我们调用成员函数,我们就不在UB领域了。

接下来我们要考虑的是构造顺序,因为成员依赖于它。该信息在 [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).

因此,成员是按照它们在 class 中声明的顺序构造的,这意味着在您的第一个示例中,您在 a 初始化后引用它,因此您不在 UB 土地中.

在您的第二个示例中,您指的是一个尚未初始化的对象,读取未初始化对象的值是未定义的行为。