构造函数继承中的奇怪行为

问题描述 投票:0回答:1

我有这个例子:

#include <iostream>
#define print(X) std::cout << X << std::endl

struct B1 {
    B1(int _i = 5): i(_i) { print("B1 constructor"); };
    int i;
};

struct B2 {
    B2(int _j = 7): j(_j) { print("B2 constructor"); }
    int j;
};

struct D : B2, B1 {
    using B1::B1;
};

int main(void)
{
    D d = D{10};
    print("B1::i = " << d.i);
    print("B2::j = " << d.j);
}

该程序的输出是:

B2 constructor
B1 constructor
B1::i = 10
B2::j = 7

根据 §11.9.4[class.inhctor.init]/1

当调用类型 B 的构造函数来初始化 a 的对象时 不同类型D(即构造函数被继承时 ([namespace.udecl])),初始化就像默认的一样进行 默认构造函数用于初始化 D 对象和每个基类 继承构造函数的类子对象,除了 B 子对象由继承的构造函数初始化如果 基类子对象将被初始化为 D 对象的一部分 ([class.base.init])。调用继承的构造函数, 包括任何参数的评估,如果 B 则被省略 子对象不会被初始化为 D 对象的一部分。这 完整的初始化被认为是单个函数调用; 在 特别是,除非省略,继承的初始化 构造函数的参数在任何初始化之前排序 D 对象的一部分。

首先,根据

§11.9.4/1
,由于
D
从基
B1(int)
继承构造函数
B1
,继承的构造函数可以初始化子对象
B1
;此外,参数
_i
在初始化
D
的任何部分之前已完全初始化,因此继承的构造函数
D::B1(int)
由重载决策选择,该重载决策通过 mem-initializer-list 初始化
B1::i
成员。

但是,该程序的输出表明

B1(int)
构造函数首先用
i
初始化成员
10
。然后,在执行
B1(int)
的主体之前,我不知道执行顺序如何跳转到
B2(int)
并使用默认参数
j
初始化成员
7
并执行打印的
B2(int)
的主体“
B2 constructor
”。然后,执行再次跳转到
B1(int)
并执行
B1
构造函数的主体。

我不确定是否发生了这种情况,但我无法理解这种情况下的执行顺序。以及标准中需要这种行为的地方。

c++ inheritance constructor language-lawyer
1个回答
0
投票

这个程序的输出表明,首先调用了 B1(int) 构造函数,将成员 i 初始化为 10。然后在 B1(int) 的主体执行之前,我不知道执行顺序是如何跳转到 B2(整数)

您是如何得出这个结论的?程序的输出仅表明两件事:

  • 构造函数 B2 在构造函数 B1 之前运行
  • 当您实例化
    D{10}
    时,构造函数 B1 被选择通过 D 类主体中的
    using
    表达式传递该参数。

我不知道标准对从多个独立基类继承时的初始化顺序有何具体规定(并且我不想在这里做出可能不正确的假设),但是您的程序以我期望的方式工作

© www.soinside.com 2019 - 2024. All rights reserved.