由于存在默认成员初始值设定项,class 类型的非平凡性

Non-triviality of class type due to presence of default member initializer(s)

以下所有标准参考均指N4659: March 2017 post-Kona working draft/C++17 DIS


正如人们所料,对非静态数据成员使用默认成员初始值设定项使得 class 非常重要:

// Well-formed according to both GCC and Clang (-std=c++17).
#include <type_traits>

struct Trivial { int a; int b; };
struct NotTrivial { int a; int b{0}; };

static_assert(std::is_trivial_v<Trivial>, "");
static_assert(!std::is_trivial_v<NotTrivial>, "");

int main() {}

(C++17) 标准的哪个段落规定 NotTrivial 是一个非平凡的 class?

以下所有标准参考均指N4659: March 2017 post-Kona working draft/C++17 DIS


[class]/6 支配什么是 琐碎的 class [摘录]:

[...] A trivial class is a class that is trivially copyable and has one or more default constructors, all of which are either trivial or deleted and at least one of which is not deleted. [...]

让我们将 琐碎的 class 要求表示如下:

  • (A) class 可简单复制,
  • (B) class 的所有默认构造函数都是微不足道的或已删除,并且
  • (C) class 至少有一个 non-deleted 默认构造函数。

如下所示,NonTrivial 满足要求 (A)(C),但未满足要求 (B),因此不是微不足道的。


要求 (A):平凡可复制[已实现]

要求 (A)[class]/6:

约束

A trivially copyable class is a class:

  • (6.1) where each copy constructor, move constructor, copy assignment operator, and move assignment operator ([class.copy], [over.ass]) is either deleted or trivial,
  • (6.2) that has at least one non-deleted copy constructor, move constructor, copy assignment operator, or move assignment operator, and
  • (6.3) that has a trivial, non-deleted destructor.

其中第一个 sub-requirement (6.1) 涵盖构造函数和赋值运算符的 琐碎性 [class.copy.ctor]/11 and [class.copy.assign]/9,分别是:

[class.copy.ctor]/11

A copy/move constructor for class X is trivial if it is not user-provided and if:

  • (11.1) class X has no virtual functions and no virtual base classes, and
  • (11.2) the constructor selected to copy/move each direct base class subobject is trivial, and
  • (11.3) for each non-static data member of X that is of class type (or array thereof), the constructor selected to copy/move that member is trivial;

otherwise the copy/move constructor is non-trivial.

[class.copy.assign]/9

A copy/move assignment operator for class X is trivial if it is not user-provided and if:

  • (9.1) class X has no virtual functions and no virtual base classes, and
  • (9.2) the assignment operator selected to copy/move each direct base class subobject is trivial, and
  • (9.3) for each non-static data member of X that is of class type (or array thereof), the assignment operator selected to copy/move that member is trivial;

otherwise the copy/move assignment operator is non-trivial.

NonTrivial.

全部满足

第二个 sub-requirement (6.2) 涵盖 构造函数和赋值运算符的存在 受限时隐式声明的特殊函数(如本例所示)分别由[class.copy.ctor]/6 and [class.copy.assign]/2控制:

[class.copy.ctor]/6

If the class definition does not explicitly declare a copy constructor, a non-explicit one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted. [...]

[class.copy.assign]/2

If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy assignment operator is defined as deleted; otherwise, it is defined as defaulted. [...]

因此,将为 NonTrivial 隐式声明一个复制构造函数和复制赋值运算符。从 [class.copy.ctor]/8 and [class.copy.assign]/4 4 可以看出,这同样适用于移动构造函数和移动赋值运算符。因此,NonTrivial 满足 sub-requirement (6.2).

第三个 sub-requirement (6.3)[class.dtor]/6:

管辖

A destructor is trivial if it is not user-provided and if:

  • (6.1) the destructor is not virtual,
  • (6.2) all of the direct base classes of its class have trivial destructors, and
  • (6.3) for all of the non-static data members of its class that are of class type (or array thereof), each such class has a trivial destructor.

Otherwise, the destructor is non-trivial.

NonTrivial 满足,因此 (A)NonTrivial.

成立

要求 (B):简单或已删除的默认构造函数[未满足]

根据 [class.ctor]/4 [摘录]:

[...] If there is no user-declared constructor for class X, a non-explicit constructor having no parameters is implicitly declared as defaulted ([dcl.fct.def]).

将为 NonTrivial class 隐式声明默认构造函数。但是,根据 [class.ctor]/6, particularly [class.ctor]/6.2,这个隐式声明的默认构造函数 并不简单 :

A default constructor is trivial if it is not user-provided and if:

  • [...]
  • (6.2) no non-static data member of its class has a default member initializer ([class.mem]), and [...]

因此 NonTrivial class 不满足 (B) 的要求,因此不是微不足道的。

我们可能注意到子句 [class.ctor]/6.2 源自 non-static 数据成员初始值设定项 N2628 的原始提案,该提案建议添加

no non-static data member of its class has an assignment-initializer, and

作为 [class.ctor] 的额外琐碎要求。


需求 (C):存在默认构造函数[已实现]

为了完整起见,我们可能会注意到 NonTrivial class 满足要求 (C),根据 [class.ctor]/4(引用于上面的部分)。