Repository navigation
CWG2820 [dcl.init.general] Zero-initialization VS default constructors #40
Description
Full name of submitter (unless configured in github; will be published with the issue): Jiang An
Reference (section label): [dcl.init.general]
Link to reflector thread (if any):
Issue description:
-
A class may be default-initializable while having more than one default constructors. In such cases, implementations don't consider any unselected default constructors during value-initialization, and may perform zero-initialization.
This strategy is slightly different from [dcl.init.general]/9 which doesn't exclude unselected defaulted constructors. Perhaps zero-initialization is desired and [dcl.init.general]/9 should be modified to match implementations. -
[dcl.init.general]/(9.1.2) seemly indicates that if the default constructor is defaulted (not user-provided) and non-trivial, then the object is zero-initialized and then default-initialized. The current wording might have some drawbacks and lead to implement divergence:
- Some implementations (gcc, icc, and clang) only perform default-initialization in this case, while others (msvc) perform both operations.
- When the default constructor is trivial, it is unobservable whether it is called after zero-initialization, so perhaps the default-initialization can be performed unconditionally.
Example:
struct Foo {
int x;
Foo() = default;
template<class T>
Foo(T = T()) {} // user-provided, also a default constructor
};
template<int N>
struct Bar {
int y;
Bar() requires (N >= 0) = default;
Bar() requires (N < 0) {} // user-provided
};
static_assert(Foo().x == 0); // passes
static_assert(Bar<0>().y == 0); // passes if supported
struct Baz {
int z;
int w = 1;
Baz() = default; // non-trivial
};
static_assert(Baz().z == 0); // passes on msvc, fails on gcc, icc, and clangSuggested resolution:
There are two mutually exclusive suggested resolutions. Option A exclusively selects zero-initialization or default-initialization (matching gcc etc.), while Option B sometimes perform both forms (matching msvc, seemly the status quo).
Option A:
Change [dcl.init.general]/(9.1) as indicated:
if
Tis a (possibly cv-qualified) class type ([class]), the semantic constraints for default-initialization are checked, and then
- if
there is no selected default constructor ([class.default.ctor]) for default-initialization, or the selected default constructor is non-trivial or deleted, then the object is default-initialized;Thas either no default constructor ([class.default.ctor]) or a default constructor that is user-provided or deleted- otherwise, the object is zero-initialized
and the semantic constraints for default-initialization are checked, and if;Thas a non-trivial default constructor, the object is default-initialized
Option B:
Change [dcl.init.general]/(9.1) as indicated:
if
Tis a (possibly cv-qualified) class type ([class]), the semantic constraints for default-initialization are checked, and then
- if
there is no selected default constructor ([class.default.ctor]) for default-initialization, or the selected default constructor is user-provided or deleted, then the object is default-initialized;Thas either no default constructor ([class.default.ctor]) or a default constructor that is user-provided or deleted- otherwise, the object is zero-initialized and
the semantic constraints for default-initialization are checked, and ifthen default-initialized;Thas a non-trivial default constructor, the object is
Activity
jensmaurer commentedon May 25, 2022
See CWG1324 and CWG1368 (resolved by CWG1301).
The latest edit to that paragraph was via CWG1507.
@frederick-vs-ja , please do me a favor and double-check that the proposed edits don't revert or otherwise negatively affect those existing (and resolved) core issues, i.e. that we're not introducing a regression.
xmh0511 commentedon May 25, 2022
I'm not sure whether the second initialization(i.e. default-initialization creates an object
o2) will end the lifetime of the objecto1that is begun by the first initialization(i.e. zero-initialization), which is regarded as a reuse of the storage occupied byo1. Anyway, [class.ctor.general] p3 says[dcl.init.general] p20
We do not like the double-initialization and the subsequent confusion caused by such initialization
See cplusplus/draft#4802 and the linked questions in that issue.
frederick-vs-ja commentedon May 25, 2022
@jensmaurer I'm sure that the suggested resolutions only change the conditions under which zero-initialization is performed. So existing resolutions won't be broken. Anyway, the semantic constraints for default-initialization are always needed to be checked.
@xmh0511 I think the wording can "magically" ensure that there is only
o1and noo2, so reuse doesn't happen.[class.ctor.general] p3 is talking about functional notation type conversion, although it was accidently made dangling by P1787.
Perhaps we should resolve double-initialization uniformly elsewhere, and discussion for delegating constructors may be needed.
xmh0511 commentedon May 25, 2022
AFAIK, each initialization will construct an object. An object cannot be initialized twice. If it were, it wouldn't make sense or conform to intuitive. Again, if the initializations of the object in this issue are the zero-initialization followed by the default-initialization, which of them starts the lifetime of the object?
frederick-vs-ja commentedon May 27, 2022
Perhaps we shouldn't apply this model to objects of trivially default constructible (and/or implicit-lifetime?) types, in order to start their lifetime as quickly as possible. Implementations can (and probably do) support zero-initialization followed by dynamic initialization, and there're probably a lot of programs depending on this.
IMO for objects (including subobjects) of trivially default constructible types, their lifetime should be started by zero-initialization; for other objects, zero-initialization is not sufficient to start their lifetime.
Anyway, I think regularization or removal of double-initailziation should be handled as another issue.
frederick-vs-ja commentedon Nov 1, 2023
CWG2820 partially addresses this (a weak form of Option B is proposed).
[-][dcl.init.general] Zero-initialization VS default constructors[/-][+]CWG2820 [dcl.init.general] Zero-initialization VS default constructors[/+]SRNissen commentedon Nov 20, 2023
How sure are you of that?
I only encountered the issue of default-after-zero initialization this weekend and haven't spent more than, at most, ten hours looking, but I cannot find any statement that an object cannot be initialized after it has been initialized.
RE https://cplusplus.github.io/CWG/issues/2820.html
The suggested change is
Which leaves my pet scenario with a double-initialization:
The order, if I got the Standard right, is:
tis list-initializedT's implicitly defaulted ctor is non-trivial because ofnt{20};tis zero-initialized, then default-initialized.And so, again,
t.ntis initialized twice - first zero-initialized during the zero-initialization oft, then initialized again with the brace-or-equal-initializer during the default construction oft.EDIT
I can tell you what resolution I would prefer here:
Value initializing an object of class type will recursively value-initialize every base class and sub-object, picking their initializers in this prioritized order:
Clear. Obvious. Probably politically impossible.
My secondary preference would be some intricate wording to indicate that value initialization does zero-initialization as before but only for sub-objects that are not mentioned member initializer lists and which do not have Default Member Initializers.
My tertiary preference is to redefine "zero-initialization" of subobjects to set values to integer literal zero unless the sub-object is present in a member-initializer-list or a default member initializer is present, in which case zero initialization picks that value instead of zero.
(Two and three have the same actual effect, it's just whether you say "don't zero-initialize sub-objects with other initializers" or "do zero-initialize sub-objecst with other initializers, but we'll redefine what zero-initialize means."