Initialization

fefe發表於2022-03-13

Initialization

  • initialized form

    • Direct-initialization
    • Copy-initialization
  • The meaning of initialization
  • Value initialize
  • Default initialize
  • Zero initialize
  • List initialize

    • narrowing conversion
  • character array initialization
  • Aggregate initialization

    • Aggregate
  • reference initialization

Based on c++20 draft n4868

initialized form

Usually when talking about initialization, you will think of the initialization value (initializer) given when a variable is used. But beyond that, there are some places where the initialization of the object happens. All of these initialization operations follow the same set of rules.

Initialization can be divided into two categories according to its form, direct-initialization and copy-initialization

Direct-initialization

  • There is no initialization value in the variable definition =

    • int i{1}; std::vector<std::string> vec(3);
  • new-initializer

    • int *p = new int(3); , initialize a new int object with 3
  • static_cast

    • static_cast initializes an object of the result type with its parameters, such as: static_cast<int>(2.0); , which initializes a int object with 2.0 .
  • Type conversion of function calls like

    • int(2.0) , where an object of type 2.0 initialized with int
  • brace-init-list in condition

    • if (Derived *pDerived{dynamic_cast<Derived*>(pBase)}) , initialize pDerived with dyanmic_cast<Drived*>(pBase)

Copy-initialization

  • The variable definition is initialized in the form of =

    • int i = 1; , initialize i with 1
  • The condition is initialized in the form of =

    • if (Derived *pDerived = dynamic_cast<Derived*>(pBase)) , initialize pDerived with dyanmic_cast<Drived*>(pBase)
  • parameter passing, function return value

    • Example:

      int foo(int i) {
        return i+1;
      }
      void bar() {
        foo(1);
      }

      Initialize the parameter 1 of foo with i , and initialize the return value (rvalue) of the function with i+1

  • throw exception, exception catch

    • Example:

      void foo() {
        std::string s{123};
        try {
          throw s;
        } cache (string &es) {
        }
      }

      In throw s; , use s initialize a temporary object. In cache (string &es) the lvalue of the temporary object is used to initialize es . Both initializations are copy initialize

  • Aggregate member initialization. In aggregate initialization, the initialization of each member is copy initialization

    • std::string str[]{"abc", "def"} , initialize str[0] with "abc" , initialize str[1] with "def" are all copy-initialize. Note that the initialization of str uses the form of direct-initialize.

The meaning of initialization

Initialization has different meanings depending on the type of the object being initialized and the form of initialization. The specific initialization meaning is as follows. Both copy-initialize and direct-initialize use the following definitions.

  • If initializer is a braced-init-list or = branced-init-list , perform list initialization
  • If the target type is a reference type, perform reference initialization
  • If the target type is char , singed char , unsigned char , char8_t , char16_t , char32_t , wchar_t array, initializer is a String-literal , perform character array initialization
  • If initializer is () , execute value-initialze
  • If the target type is an array, the initializer should be ( pxpression-list ) , and there should be no more elements than the array length. The elements in the array will in turn be copy-initialized by the values in expression-list . value-initialize elements for which no value is provided (array length is longer than expression-list )
  • If the target type is a class type:

    • If initializer is an rvalue and is of the same type as the target (ignoring cv-qualifier), initializer is used to initialize the target object
    • If 1) this is direct initialization; or 2) this is copy initialization and the source type is the same as the target type, or a derived class of the target type, then use constructor initialization

      • If the only available constructor can be found, then that constructor is used to initialize the object
      • If there is no available constructor, and the target type is an aggregate class, the initializer is ( expression-list ) , then use the values in expression-list to copy the corresponding elements in turn. Elements with uninitialized values perform value initialize.
      • Otherwise, the program is illegal
    • Otherwise, try user-defined type conversions (including constructors). A program that cannot find a suitable type conversion is illegal. The result of the type conversion will be used to direct initialize the target object.
  • Otherwise, if the source type is a class type, a user-defined type conversion will be attempted. A program that cannot find a suitable type conversion is illegal.
  • Otherwise, if this is a direct initialization, the original type is std::nullptr_t , and the target type is bool , then the target object is initialized to false
  • Otherwise, if necessary, a standard conversion sequence can be used to convert the source object to the target type. If it cannot be converted, the program is illegal.

Value initialize

For a type value initialize means:

  • If it is a class type, then

    • If the class has no default constructor, or has a user-provided or deleted default constructor, the object is default initialized
    • Otherwise, the object is zero initialized; and, if the class has a non-trivial default constructor, the object is value initialized.

      • E.g

        #include <string>
        struct A{
          int a;
          std::string s;
        };
        int main()
        {
          A a_obj{}; // a_obj.a == 0
        }

        The class A has a default constructor (automatically generated), the default constructor is neither user-provided nor deleted, so it enters this processing.
        a_obj is zero initialized. Also, since its default constructor is non-trivial (it contains a std::string member), it is further default constructed.
        Since zero initialize exists, a_obj.a == 0 .

  • If the target object type is an array, each element of the array is initialized by value
  • Otherwise, the object is zero initialized

Default initialize

The meaning of default initialize for a type is:

  • For class types, the default constructor is called.
  • For array types, each element is initialized by default
  • Others, do not perform initialization operations

Zero initialize

The meaning of default initialize for a type is:

  • scalar type is initialized to 0
  • For non-union class types, its padding is initialized to 0, non-static data members, the base class is zero initialize
  • For union, padding is initialized to 0, and the first non-static member is zero initialized
  • For arrays, each element is zero initialized
  • For references, do not initialize

List initialize

  • If brace-init-list contains designated-initializer-list , the target type must be an aggregate class, and the members must appear in the same order as they are defined. Perform aggregate initialize
  • If the target is an aggregate class (aggregate class), the initializer list contains only one element, whose type is the same as the target type or a derived class, then use that element to initialize the target object.
  • If the target type is an array, the initializer list contains only one element, and is a string literal of the appropriate type, perform character array initialization.
  • If the target type is an aggregate class, perform aggregate initialization
  • If the initializer list is empty, the target type is a class type with a default constructor, and the target object is initialized by value
  • If the target type is std::initializer_list<E> : Create an array of type const E[N] , where each element is copy initialized with an element in the initializer list, and the target object will reference this array. (If any element initialization requires narrowing conversion, the program is illegal)
  • If the target type is a class type, use the constructor initialization. (If narrowing conversion is required during parameter initialization, the program is illegal)
  • If the target type is an enumeration with a determined underlying type, the intiiailizer list contains only one element, which can be implicitly converted to the underlying type of the target type, and the initialization is direct initialization, then the value of the target object is converted to the source object The value after the underlying type of the target type. (It cannot be copy initialize; if narrawing conversion is required, the program is illegal)
  • If the initialize list has a unique element, the target type is not a reference or a reference related to the source type, the target object (or reference) is initialized by this element (if narrowing conversion is required, the program is illegal)
  • If the target type is a reference, use the initializer to execute copy list initialize to initialize a prvalue of the referenced type, and the target reference is directly initialized by the prvalue
  • If the initializer list is empty, the target object is initialized by value
  • Other, the program is illegal

narrowing conversion

narrowing conversions include:

  • Conversion from floating point type to integer
  • Conversion from long double to double or float , from double to float , unless the source value is a constant expression and it is representable in the destination type (including the case where it is representable but not exactly representable)
  • A conversion from an integer or unscoped enueration type to a floating-point type, unless the source value is a constant expression, the result of the conversion is representable in the target type, and its value is unchanged when the result of the conversion is reconverted to the source type
  • A conversion from an integer or unscoped enueration type to an integer that cannot represent all values in the source type unless the source value is a constant expression whose value is representable in the target type after integral promotion
  • from pointer or pointer-to-member type to bool

character array initialization

Character arrays can be initialized with string literals. The type of the character array needs to match the string literal type. The elements in the character array are initialized with the corresponding elements in the string literal.

String literals (including '\0' at the end) cannot be longer than character arrays. If the string literal is shorter, excess array elements will be zero initialized.

Aggregate initialization

Aggregate

An array or class (union is also a class) that satisfies the following conditions is called an aggregate:

  • No constructor declared, or inherited (note: use using ) constructor
  • No private or protected non-static data members
  • no virtual function
  • No virtual, private or protected base classes

The members of aggregate are:

  • For an array, each element of it
  • For classes, each direct base class in declaration order, and each non-static data member in declaration order (if the class has an anonymous union, the anonymous union itself is a member of aggregate, but the members of the anonymous union no)

For members for which a value is explicitly provided, the value will be used for copy initialize. If a narrowing conversion is required, the procedure is illegal. For anonymous unions, only one of the members can be initialized.

For non-union aggregate members that are not explicitly initialized:

  • If the member declares a default member initializer, use this value to initialize
  • If the member is not a reference, use {} copy initialize
  • Otherwise, the program is illegal

When a union aggregate is initialized with {} :

  • If any member (variant member) has a default member initializer, use this value to initialize this member
  • Otherwise, the first member of the union is initialized with {}

If the members of aggregate are also aggregates, { , } can be omitted from the initialization list of child aggregates (in some cases). Empty {} cannot be omitted. If omitted, all subsequent initializers are used to initialize the child aggregate until all members of the child aggregate have initializers. The remaining initializers are used to initialize the remaining members of the parent aggregate.

reference initialization

First of all, we need to introduce two concepts:

  • Type CV1 T1 and CV2 T2 a reference related, showing T1 with T2 similar to (approximately equal to the same), or T1 is T2 base class.
  • Type CV1 T1 and CV2 T2 is represented by reference compitable point CV2 T2 pointer conversion sequence can be converted to point (standard conversion sequence) by a standard CV1 T1 pointer. (approximately equal to cv1 > cv2 , and T1 is the same as T2 , or T2 is the same (accessible and unambiguous) base class

The referencing rules for initializing cv1 T2 with the value of cv2 T1 are as follows:

  • The target is an lvalue reference and one of the following is true:

    • initialize is an lvalue, cv1 T1 and cv2 T2 are reference compitable, the reference is bound to this value (or its base class subobject)
    • T2 is the class, but T1 and T2 not reference related, but T2 can be converted into a CV3 T3 left values, and CV1 T1 with CV3 T3 is a reference compitable, the reference is bound to convert after T3 of lvalue (or its base class subobject)
  • Otherwise, if the target is a non-const lvalue reference, or a volatile lvalue reference, the program is illegal
  • Otherwise: (the following is bound to rvalue, prvalue will be converted to glvalue)

    • If the initialization value is an rvalue (not a bitfield), or a function, and cv1 T1 and cv2 162348259284af T2 is a reference compitable, the reference is bound to that value (or its base class subobject)
    • T2 a class, but T1 with T2 not reference related, but T2 can be converted to a CV3 T3 rvalue (or function), and CV1 T1 and CV3 T3 is the reference compitable, reference is bound to the converter After the T3 of 062348259284ef (or its base class subobject)
  • otherwise:

    • If T1 or T2 is a class, and T1 and T2 not reference related, try to initialize an object of cv1 T1 with the initialization value through custom conversion (the initialization program is illegal). The result of the conversion will be used to initialize the target reference.
    • Otherwise, the initialization value will be implicitly converted to cv1 T1 . The result of a reference bound value conversion.

      • If T1 and T2 are reference related, then cv1 should be greater than cv2 , and rvalue reference cannot be initialized with lvalue.

    E.g:

    struct Banana { };
    struct Enigma { operator const Banana(); };
    struct Alaska { operator Banana&(); };
    void enigmatic() {
      typedef const Banana ConstBanana;
      Banana &&banana1 = ConstBanana(); // error
      Banana &&banana2 = Enigma(); // error
      Banana &&banana3 = Alaska(); // error
    }
    const double& rcd2 = 2; // rcd2 refers to temporary with value 2.0
    double&& rrd = 2; // rrd refers to temporary with value 2.0
    const volatile int cvi = 1;
    const int& r2 = cvi; // error: cv-qualifier dropped
    struct A { operator volatile int&(); } a;
    const int& r3 = a; // error: cv-qualifier dropped
    // from result of conversion function
    double d2 = 1.0;
    double&& rrd2 = d2; // error: initializer is lvalue of related type
    struct X { operator int&(); };
    int&& rri2 = X(); // error: result of conversion function is lvalue of related type
    int i3 = 2;
    double&& rrd3 = i3; // rrd3 refers to temporary with value 2.0

相關文章