All standard references below refers to N4659: March 2017 post-Kona working draft/C++17 DIS.
As one may expect, the use of default member initializers for non-static data members makes a class non-trivial:
// 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() {}
What passage of the (C++17) standard governs that NotTrivial is a non-trivial class?
All standard references below refers to N4659: March 2017 post-Kona working draft/C++17 DIS.
[class]/6 governs what is a trivial class [extract]:
[...] 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. [...]
Let's denote the trivial class requirements as follows:
As will be shown below, NonTrivial fulfills requirements (A) and (C), but fails requirement (B), and is thus not trivial.
Requirement (A) is governed by [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.
where the first sub-requirement (6.1) covering triviality of constructors and assignment operators is governed by [class.copy.ctor]/11 and [class.copy.assign]/9, respectively:
[class.copy.ctor]/11
A copy/move constructor for class
Xis trivial if it is not user-provided and if:
- (11.1) class
Xhas 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
Xthat 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
Xis trivial if it is not user-provided and if:
- (9.1) class
Xhas 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
Xthat 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.
which are all fulfilled by NonTrivial.
The second sub-requirement (6.2) covering existence of constructors and assignment operators, when restricted to implicitly declared special functions (as is the case for this example), is governed by [class.copy.ctor]/6 and [class.copy.assign]/2, respectively:
[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. [...]
Thus, a copy constructor and copy assignment operator will be implicitly declared for NonTrivial. It follows from [class.copy.ctor]/8 and [class.copy.assign]/4 4that the same holds for for the move constructor and move assignment operator, respectively. Thus, NonTrivial fulfills the sub-requirement (6.2).
The third sub-requirement (6.3) is governed by [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.
which is fulfilled by NonTrivial, and thus requirement (A) holds for NonTrivial.
As governed by [class.ctor]/4 [extract]:
[...] 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]).
a default constructor will be implicitly declared for the NonTrivial class. However, as governed by [class.ctor]/6, particularly [class.ctor]/6.2, this implicitly declared default constructor is not trivial:
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 [...]
And thus the NonTrivial class fails the requirement (B), and is thus not trivial.
We may note that the clause [class.ctor]/6.2 originated from the original proposal for non-static data member initializers, N2628, which proposed adding
no non-static data member of its class has an assignment-initializer, and
as an additional triviality requirement to [class.ctor].
For completeness, we may note that the NonTrivial class fulfills requirement (C), as per [class.ctor]/4 (quoted to in the section above).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With