Default, value and zero initialization mess

人走茶凉 提交于 2019-11-26 18:48:57

问题


I am very confused about value- & default- & zero-initialization. and especially when they kick in for the different standards C++03 and C++11 (and C++14).

I am quoting and trying to extend a really good answer Value-/Default-/Zero- Init C++98 and C++03 here to make it more general as it would help a lot of users if somebody could help fill out the needed gaps to have a good overview about what happens when?

The full insight by examples in a nutshell:

Sometimes the memory returned by the new operator will be initialized, and sometimes it won't depending on whether the type you're newing up is a POD (plain old data), or if it's a class that contains POD members and is using a compiler-generated default constructor.

  • In C++1998 there are 2 types of initialization: zero- and default-initialization
  • In C++2003 a 3rd type of initialization, value-initialization was added.
  • In C++2011/C++2014 only list-initialization was added and the rules for value-/default-/zero-initialization changed a bit.

Assume:

struct A { int m; };                     
struct B { ~B(); int m; };               
struct C { C() : m(){}; ~C(); int m; };  
struct D { D(){}; int m; };             
struct E { E() = default; int m;} /** only possible in c++11/14 */  
struct F {F(); int m;}  F::F() = default; /** only possible in c++11/14 */

In a C++98 compiler, the following should occur:

  • new A - indeterminate value (A is POD)
  • new A()- zero-initialize
  • new B - default construct (B::m is uninitialized, B is non-POD)
  • new B() - default construct (B::m is uninitialized)
  • new C - default construct (C::m is zero-initialized, C is non-POD)
  • new C() - default construct (C::m is zero-initialized)
  • new D - default construct (D::m is uninitialized, D is non-POD)
  • new D() - default construct? (D::m is uninitialized)

In a C++03 conformant compiler, things should work like so:

  • new A - indeterminate value (A is POD)
  • new A() - value-initialize A, which is zero-initialization since it's a POD.
  • new B - default-initializes (leaves B::m uninitialized, B is non-POD)
  • new B() - value-initializes B which zero-initializes all fields since its default ctor is compiler generated as opposed to user-defined.
  • new C - default-initializes C, which calls the default ctor. (C::m is zero-initialized, C is non-POD)
  • new C() - value-initializes C, which calls the default ctor. (C::m is zero-initialized)
  • new D - default construct (D::m is uninitialized, D is non-POD)
  • new D() - value-initializes D?, which calls the default ctor (D::m is uninitialized)

Italic values and ? are uncertainties, please help to correct this :-)

In a C++11 conformant compiler, things should work like so:

??? (please help if I start here it will anyway go wrong)

In a C++14 conformant compiler, things should work like so: ??? (please help if I start here it will anyway go wrong) (Draft based on answer)

  • new A - default-initializes A, compiler gen. ctor, (leavs A::m uninitialized) (A is POD)
  • new A() - value-initializes A, which is zero-initialization since 2. point in [dcl.init]/8

  • new B - default-initializes B, compiler gen. ctor, (leavs B::m uninitialized) (B is non-POD)

  • new B() - value-initializes B which zero-initializes all fields since its default ctor is compiler generated as opposed to user-defined.
  • new C - default-initializes C, which calls the default ctor. (C::m is zero-initialized, C is non-POD)
  • new C() - value-initializes C, which calls the default ctor. (C::m is zero-initialized)
  • new D - default-initializes D (D::m is uninitialized, D is non-POD)
  • new D() - value-initializes D, which calls the default ctor (D::m is uninitialized)
  • new E - default-initializes E, which calls the comp. gen. ctor. (E::m is uninitialized, E is non-POD)
  • new E() - value-initializes E, which zero-initializes E since 2 point in [dcl.init]/8 )
  • new F - default-initializes F, which calls the comp. gen. ctor. (F::m is uninitialized, F is non-POD)
  • new F() - value-initializes F, which default-initializes F since 1. point in [dcl.init]/8 (F ctor function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration. Link)

回答1:


C++14 specifies initialization of objects created with new in [expr.new]/17 ([expr.new]/15 in C++11, and the note wasn't a note but normative text back then):

A new-expression that creates an object of type T initializes that object as follows:

  • If the new-initializer is omitted, the object is default-initialized (8.5). [ Note: If no initialization is performed, the object has an indeterminate value. — end note ]
  • Otherwise, the new-initializer is interpreted according to the initialization rules of 8.5 for direct-initialization.

Default-initialization is defined in [dcl.init]/7 (/6 in C++11, and the wording itself has the same effect):

To default-initialize an object of type T means:

  • if T is a (possibly cv-qualified) class type (Clause 9), the default constructor (12.1) for T is called (and the initialization is ill-formed if T has no default constructor or overload resolution (13.3) results in an ambiguity or in a function that is deleted or inaccessible from the context of the initialization);
  • if T is an array type, each element is default-initialized;
  • otherwise, no initialization is performed.

Thus

  • new A solely causes As default constructor to be called, which does not initialize m. Indeterminate value. Should be the same for new B.
  • new A() is interpreted according to [dcl.init]/11 (/10 in C++11):

    An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized.

    And now consider [dcl.init]/8 (/7 in C++11†):

    To value-initialize an object of type T means:

    • if T is a (possibly cv-qualified) class type (Clause 9) with either no default constructor (12.1) or a default constructor that is user-provided or deleted, then the object is default-initialized;
    • if T is a (possibly cv-qualified) class type without a user-provided or deleted default constructor, then the object is zero-initialized and the semantic constraints for default-initialization are checked, and if T has a non-trivial default constructor, the object is default-initialized;
    • if T is an array type, then each element is value-initialized;
    • otherwise, the object is zero-initialized.

    Hence new A() will zero-initialize m. And this should be equivalent for A and B.

  • new C and new C() will default-initialize the object again, since the first bullet point from the last quote applies (C has a user-provided default constructor!). But, clearly, now m is initialized in the constructor in both cases.


† Well, this paragraph has slightly different wording in C++11, which does not alter the result:

To value-initialize an object of type T means:

  • if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
  • if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object is zero-initialized and, if T’s implicitly-declared default constructor is non-trivial, that constructor is called.
  • if T is an array type, then each element is value-initialized;
  • otherwise, the object is zero-initialized.



回答2:


The following answer extends the answer https://stackoverflow.com/a/620402/977038 which would serve as a reference for C++ 98 and C++ 03

Quoting the answer

  1. In C++1998 there are 2 types of initialization: zero and default
  2. In C++2003 a 3rd type of initialization, value initialization was added.

C++11 (In reference to n3242)

Initializers

8.5 Initializers [dcl.init] specifies that a variable POD or non POD can be initialized either as brace-or-equal-initializer which can either be braced-init-list or initializer-clause aggregately referred to as brace-or-equal-initializer or using ( expression-list ). Previous to C++11, only (expression-list) or initializer-clause was supported though initializer-clause was more restricted then what we have in C++11. In C++11, initializer-clause now supports braced-init-list apart from assignment-expression as was in C++03. The following grammar summarizes the new supported clause, where the part is bold is newly added in the C++11 standard.

initializer:
&nbsp&nbsp&nbsp&nbspbrace-or-equal-initializer
&nbsp&nbsp&nbsp&nbsp( expression-list )
brace-or-equal-initializer:
&nbsp&nbsp&nbsp&nbsp= initializer-clause
&nbsp&nbsp&nbsp&nbspbraced-init-list
initializer-clause:
&nbsp&nbsp&nbsp&nbspassignment-expression
&nbsp&nbsp&nbsp&nbspbraced-init-list
initializer-list:
&nbsp&nbsp&nbsp&nbspinitializer-clause ...opt
&nbsp&nbsp&nbsp&nbspinitializer-list , initializer-clause ...opt**
braced-init-list:
&nbsp&nbsp&nbsp&nbsp{ initializer-list ,opt }
&nbsp&nbsp&nbsp&nbsp{ }

Initialization

Like C++03, C++11 still supports three form of initialize


Note

The part highlighted in bold has been added in C++11 and the one that is striked out has been removed from C++11.

  1. Initializer Type:8.5.5 [dcl.init] _zero-initialize_

Performed in the following cases

  • Objects with static or thread storage duration are zero-initialized
  • If there are fewer initializers than there are array elements, each element not explicitly initialized shall be zero-initialized
  • During value-initialize, if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object is zero-initialized.

To zero-initialize an object or reference of type T means:

  • if T is a scalar type (3.9), the object is set to the value 0 (zero), taken as an integral constant expression, converted to T;
  • if T is a (possibly cv-qualified) non-union class type, each non-static data member and each base-class subobject is zero-initialized and padding is initialized to zero bits;
  • if T is a (possibly cv-qualified) union type, the object’s first non-static named data member is zero initialized and padding is initialized to zero bits;
  • if T is an array type, each element is zero-initialized;
  • if T is a reference type, no initialization is performed.

2. Initializer Type: 8.5.6 [dcl.init] _default-initialize_

Performed in the following cases

  • If the new-initializer is omitted, the object is default-initialized; if no initialization is performed, the object has indeterminate value.
  • If no initializer is specified for an object, the object is default-initialized, except for Objects with static or thread storage duration
  • When a base class or a non-static data member is not mentioned in a constructor initializer list and that constructor is called.

To default-initialize an object of type T means:

  • if T is a (possibly cv-qualified) non-POD class type (Clause 9), the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
  • if T is an array type, each element is default-initialized;
  • otherwise, no initialization is performed.

Note Until C++11, only non-POD class types with automatic storage duration were considered to be default-initialized when no initializer is used.


3. Initializer Type: 8.5.7 [dcl.init] _value-initialize_

  1. When an object(nameless temporary, named variable, dynamic storage duration or non-static data member) whose initializer is an empty set of parentheses, i.e., () or braces {}

To value-initialize an object of type T means:

  • if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);
  • if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then every non-static data member and base-class component of T is value-initialized; then the object is zero-initialized and, if T’s implicitly-declared default constructor is non-trivial, that constructor is called.
  • if T is an array type, then each element is value-initialized;
  • otherwise, the object is zero-initialized.

So to summarize

Note The relevant quotation from the standard is highlighted in bold

  • new A : default-initializes (leaves A::m uninitialized)
  • new A() : Zero-initialize A, as the value initialized candidate does not have a user-provided or deleted default constructor. if T is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object is zero-initialized and, if T’s implicitly-declared default constructor is non-trivial, that constructor is called.
  • new B : default-initializes (leaves B::m uninitialized)
  • new B() : value-initializes B which zero-initializes all fields; if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the default constructor for T is called
  • new C : default-initializes C, which calls the default ctor. if T is a (possibly cv-qualified) class type (Clause 9), the default constructor for T is called, Moreover If the new-initializer is omitted, the object is default-initialized
  • new C() : value-initializes C, which calls the default ctor. if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the default constructor for T is called. Moreover, An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized


来源:https://stackoverflow.com/questions/29765961/default-value-and-zero-initialization-mess

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!