By adding the constructor pod_wctor( const int setx, const int sety, const int setz ) : x(setx), y(sety), z(setz) { }
to your class, it loses its status as an aggregate: [dcl.init.aggregate]/1
An aggregate is an array or a class (Clause 9) with no user-provided constructors
It still is a POD, because a trivial class only needs to have no non-trivial default ctors: [class]/6
A trivial class is a class that has a default constructor (12.1), has no non-trivial default constructors, and is trivially copyable.
The interesting point here is that for an aggregate, the list-initialization pod peenit{};
performs aggregate-initialization:
List-initialization of an object or reference of type
T
is defined as follows:
- If
T
is an aggregate, aggregate initialization is performed (8.5.1). [...]- Otherwise, if the initializer list has no elements and
T
is a class type with a default constructor, the object is value-initialized.
(Note: this is the revised order. AFAIK, in the Standard itself, the order of those two points is reversed, which must be a defect, as every aggregate has a default ctor -- the implicitly declared&defined one.)
Aggregate-initialization leads to value-initialization of the int
members: [dcl.init.aggr]/7
If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member not explicitly initialized shall be initialized from an empty initializer list
and [dcl.init.list]/3 "Otherwise, if the initializer list has no elements, the object is value-initialized"
However, for the non-aggregate pod_wctor
, the list-initialization pod_wctor podtornit{}
directly performs value-initialization, which calls the default ctor. [class.ctor]/6 specifies:
The implicitly-defined default constructor performs the set of initializations of the class that would be performed by a user-written default constructor for that class with no ctor-initializer (12.6.2) and an empty compound-statement.
and in [class.base.init]/8, we find:
In a non-delegating constructor, if a given non-static data member or base class is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then
- [...]
- otherwise, the entity is default-initialized (8.5).
The default ctor itself does not guarantee zeroing of the members because it only does default-initialization of the members.
The difference between default- and value-initialization: [dcl.init]
[7] To default-initialize an object of type
T
means:
- if
T
is a (possibly cv-qualified) class type, the default constructor forT
is called [...]- [...]
- otherwise, no initialization is performed.
[...]
[8] To value-initialize an object of type
T
means:
- if
T
is a (possibly cv-qualified) class type with either no default constructor or a default constructor that is user-provided or deleted, then the object is default-initialized;- if
T
is a (possibly cv-qualified) non-union class type without a user-provided or deleted default constructor, then the object is zero-initialized and, ifT
has a non-trivial default constructor, default-initialized;- [...]
- otherwise, the object is zero-initialized.
(I admit this confused me, and I had to revise my answer.)
pod_wctor
has a default-constructor that is not user-provided. Therefore, for the list-initialization pod_wctor podtornit{}
, the second bullet of value-initialization applies. The object podtornit
itself is zero-initialized, which leads to a zero-initialization of its members. Only then will it be default-initialized, and the default ctor will be called. The latter does nothing, but the former guarantees the members will be zeroed.