混合内联和构造函数初始化的初始化顺序



[这个问题源于这个问题]

请考虑以下程序:

#include <iostream>
struct Foo
{
const size_t size;
int* arr{ create_arr(size) };
Foo(size_t s)
: size{ set_size(s) }
{
}
static int* create_arr(size_t s)
{
std::cout << "Creating array with size " << s << 'n';
return new int[s];
}
static size_t set_size(size_t s)
{
std::cout << "Setting size to " << s << 'n';
return s;
}
};
int main()
{
Foo f(10);
}

arr的内联初始化取决于size的值。但是size的值仅在构造函数初始值设定项列表中初始化。

GCC 和 Clang 都正确处理它,如果镜像了sizearr的声明顺序,则会投诉。我还听说MSVC会做"正确"的事情(根据另一个问题中的评论)。

我的问题是:这是明确的定义吗?

我知道初始化是按声明顺序完成的,但它是否也包括内联初始化?

我知道

初始化是按声明顺序完成的,但是这样做 还包括内联初始化?

是的,它必须,请参阅class.base.init :

13 在非委托构造函数中,初始化在 以下顺序: (13.1) — 首先,并且仅适用于 大多数派生类 (4.5) 的虚拟基类在 它们出现在深度优先的从左到右遍历上的顺序 基类的有向无环图,其中"从左到右"是 派生类中基类的出现顺序 基本说明符列表。(13.2) — 然后,直接基类为 按照声明顺序初始化,因为它们出现在 基本说明符列表(无论 MEM 初始值设定项的顺序如何)。 (13.3) —然后,按以下顺序初始化非静态数据成员 它们是在类定义中声明的(同样,无论 内存初始值设定项的顺序)。(13.4) — 最后, 执行构造函数主体的复合语句。[ 注: 声明令是强制性的,以确保基地和成员子

尽管这里的标准不太具体,但这应该完全足够,因为默认成员初始值设定项是初始值设定项。类定义中的顺序始终是这里的基础。在初始化方面,这里有一个例外将与标准的许多其他部分非常矛盾。

在本节中还附加了注释,该标准强调通过关注销毁:

[注:申报令的授权是为了确保基础和 成员子对象的销毁顺序与 初始化。— 尾注 ]

所以这里根本没有进一步的解释空间。

是的,它的定义很好。我将在这里使用 C++4860 的 N20 草案作为参考。

11.10.2 初始化基和成员 [class.base.init] §9 说(强调我的):

在非委托构造函数中,如果给定的潜在构造子对象不是由内存初始值设定项指定的 id(包括没有 mem-initializer-list 的情况,因为构造函数没有 ctorinitializer), then
(9.1) — 如果实体是具有默认成员初始值设定项 (11.4) 和
任一 (9.1.1) 的非静态数据成员 — 构造函数的类是一个联合 (11.5)...
(9.1.2) — 构造函数的类不是联合,如果实体是匿名联合的成员... (9.2) — 否则,如果实体是匿名联合或变体成员 (11.5.1),...
(9.3) — 否则,实体默认初始化 (9.4)。

这与 11.4 类成员 [class.mem] §10 一致

大括号或等于初始值设定项应仅出现在数据成员的声明中。(对于静态数据成员, 见11.4.8.2;对于非静态数据成员,请参阅 11.10.2 和 9.4.1)。非静态的大括号或等于初始值设定项 数据成员为成员指定默认成员初始值设定项...

最后,我们在 11.10.2 初始化基和成员 [class.base.init] §13 说(强调我的):

13

在非委托构造函数中,初始化按以下顺序进行:
(13.1) — 首先,并且仅对于派生最多的类 (6.7.2) 的构造函数,虚拟基类初始化于 它们出现在基类有向无环图的深度优先从左到右遍历上的顺序, 其中"从左到右"是基类在派生类基说明符列表中的出现顺序。
(13.2) — 然后,直接基类按照它们出现在基说明符列表中的声明顺序进行初始化 (无论 mem 初始值设定项的顺序如何)。
(13.3) — 然后,非静态数据成员按照它们在类定义中声明的顺序进行初始化(同样,无论 mem 初始值设定项的顺序如何)。
(13.4) — 最后,执行构造函数主体的复合语句。
[注:宣布令的授权是确保在 初始化顺序相反。—尾注]

我的理解是,无论类的成员是否使用mem 初始值设定项,它们都按声明顺序初始化。如果成员没有 mem 初始值设定项,但具有大括号或等于初始值设定项,则在初始化之前声明的成员之后,它将从该大括号或等于初始值设定项默认初始化。

因此,如果我正确阅读了 11.4 和 11.2 子句,则有问题的代码具有明确定义的行为。

最新更新