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 newint
object with3
static_cast
static_cast
initializes an object of the result type with its parameters, such as:static_cast<int>(2.0);
, which initializes aint
object with2.0
.
Type conversion of function calls like
int(2.0)
, where an object of type2.0
initialized withint
brace-init-list in condition
if (Derived *pDerived{dynamic_cast<Derived*>(pBase)})
, initializepDerived
withdyanmic_cast<Drived*>(pBase)
Copy-initialization
The variable definition is initialized in the form of
=
int i = 1;
, initializei
with1
The condition is initialized in the form of
=
if (Derived *pDerived = dynamic_cast<Derived*>(pBase))
, initializepDerived
withdyanmic_cast<Drived*>(pBase)
parameter passing, function return value
Example:
int foo(int i) { return i+1; } void bar() { foo(1); }
Initialize the parameter
1
offoo
withi
, and initialize the return value (rvalue) of the function withi+1
throw exception, exception catch
Example:
void foo() { std::string s{123}; try { throw s; } cache (string &es) { } }
In
throw s;
, uses
initialize a temporary object. Incache (string &es)
the lvalue of the temporary object is used to initializees
. Both initializations are copy initialize
Aggregate member initialization. In aggregate initialization, the initialization of each member is copy initialization
std::string str[]{"abc", "def"}
, initializestr[0]
with"abc"
, initializestr[1]
with"def"
are all copy-initialize. Note that the initialization ofstr
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 isbool
, then the target object is initialized tofalse
- 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 astd::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 typeconst 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
todouble
orfloat
, fromdouble
tofloat
, 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 CV2T2
a reference related, showingT1
withT2
similar to (approximately equal to the same), orT1
isT2
base class. - Type CV1
T1
and CV2T2
is represented by reference compitable point CV2T2
pointer conversion sequence can be converted to point (standard conversion sequence) by a standard CV1T1
pointer. (approximately equal to cv1 > cv2 , andT1
is the same asT2
, orT2
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 cv2T2
are reference compitable, the reference is bound to this value (or its base class subobject) T2
is the class, butT1
andT2
not reference related, butT2
can be converted into a CV3T3
left values, and CV1T1
with CV3T3
is a reference compitable, the reference is bound to convert afterT3
of lvalue (or its base class subobject)
- initialize is an lvalue, cv1
- 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 162348259284afT2
is a reference compitable, the reference is bound to that value (or its base class subobject) T2
a class, butT1
withT2
not reference related, butT2
can be converted to a CV3T3
rvalue (or function), and CV1T1
and CV3T3
is the reference compitable, reference is bound to the converter After theT3
of 062348259284ef (or its base class subobject)
- If the initialization value is an rvalue (not a bitfield), or a function, and cv1
otherwise:
- If
T1
orT2
is a class, andT1
andT2
not reference related, try to initialize an object of cv1T1
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
andT2
are reference related, then cv1 should be greater than cv2 , and rvalue reference cannot be initialized with lvalue.
- If
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
- If