在初始化列表中调用私有函数是否属于未定义行为?

5

Consider the following code:

struct Calc
{
   Calc(const Arg1 & arg1, const Arg2 & arg2, /* */ const ArgN & argn) :
      arg1(arg1), arg2(arg2), /* */ argn(argn), 
      coef1(get_coef1()), coef2(get_coef2()) 
   {
   }

   int Calc1();
   int Calc2();
   int Calc3();

private:
  const Arg1 & arg1;
  const Arg2 & arg2;
  // ...
  const ArgN & argn;

  const int coef1; // I want to use const because 
  const int coef2; //      no modification is needed.

  int get_coef1() const {
     // calc coef1 using arg1, arg2, ..., argn;
     // undefined behavior?     
  }
  int get_coef2() const {
     // calc coef2 using arg1, arg2, ..., argn and coef1;
     // undefined behavior?
  }

};

当我调用get_coef1get_coef2时,struct Calc并没有完全定义。这段代码有效吗?我会得到UB吗?

它能工作...但你将自己置于麻烦之中。当维护者来临时,会在系数后添加 argZ,然后在计算中使用 argZ,那么会发生什么呢?你就会遇到UB(未定义行为)问题... - Matthieu M.
3个回答

8
12.6.2.8:成员函数(包括虚成员函数,10.3)可以在正在构造的对象中调用。同样地, 正在构造的对象可以是typeid运算符(5.2.8)或dynamic_cast(5.2.7)的操作数。但是, 如果在所有基类的mem-initializers完成之前在ctor-initializer(或从ctor-initializer直接或间接调用的函数中)执行这些操作,则操作的结果未定义。
因此,您可以通过这种方式初始化类成员,但不能初始化基类。而且,正如其他人指出的那样,如果您的函数使用了一些成员变量的值,则应注意成员初始化顺序。

+1 是因为它更贴切于原帖作者实际想要了解的内容。 - Björn Pollex

3

由于计算所依赖的变量在调用时已经被初始化,因此不应该出现未定义行为。有关信息,请参见问题。


3
请记住,成员的初始化顺序是根据它们的声明顺序而不是它们在初始化列表中出现的顺序进行的。这里也一样,但请注意,仅仅将const int coef1;放置在const Arg1& arg1;之前(并保持初始化列表不变)导致未定义行为。 - Tyler McHenry
我的问题不是关于初始化的顺序。它们声明的顺序是正确的。我问的是关于get_calc1函数。例如,this指针是否有效?arg2已被初始化,是的,没错。但我能在get_calc1中访问arg2吗? - Alexey Malistov
1
调用仅涉及已初始化成员属性的成员函数是完全可以的。 - Mark B

0

不是未定义的,但您必须确信这些成员函数仅使用初始化值。还要注意,值是按类中出现的顺序而不是初始化列表中出现的顺序进行初始化。例如:

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

在那个构造函数中,变量的初始化顺序是a、b、然后c,列表中的顺序并不重要。因此,如果您希望使用bc上的某些计算来初始化a的值,则需要将a的声明移动到bc之后的某个位置。

谢谢。但我的问题不是关于初始化顺序的。你可能已经注意到了,顺序是正确的。 - Alexey Malistov
我在前几个单词中回答了这个问题。只要顺序正确,它就不是未定义的,而在你的情况下,顺序是正确的,所以你没问题。 - Peter Alexander

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接