Default-initialization
This is the initialization performed when an object is constructed with no initializer.
Syntax
T object ;
|
(1) | ||||||||
new T
|
(2) | ||||||||
Explanation
Default-initialization is performed in three situations:
The effects of default-initialization are:
- if
Tis a (possibly cv-qualified) non-POD(until C++11) class type, the constructors are considered and subjected to overload resolution against the empty argument list. The constructor selected (which is one of the default constructors) is called to provide the initial value for the new object; - if
Tis an array type, every element of the array is default-initialized;
|
(since C++26) |
- otherwise, no initialization is performed (see notes).
|
Only (possibly cv-qualified) non-POD class types (or arrays thereof) with automatic storage duration were considered to be default-initialized when no initializer is used. Scalars and POD types with dynamic storage duration were considered to be not initialized (since C++11, this situation was reclassified as a form of default-initialization). |
(until C++11) |
Default-initialization of a const object
If a program calls for the default-initialization of an object of a const-qualified type T, T must be a const-default-constructible type.
A type T is const-default-constructible if
|
(since C++26) |
|
(since C++11) |
- Default-initialization of
Twould invoke a user-provided constructor ofT(not inherited from a base class)(since C++11). Tis a class type such that:
|
(until C++11) |
|
(since C++11) |
Tis an array of const-default-initializable type.
Indeterminate and erroneous values
|
When storage for an object with automatic or dynamic storage duration is obtained, the object has an indeterminate value. If no initialization is performed for an object, that object retains an indeterminate value until that value is replaced. |
(until C++26) |
|
When storage for an object with automatic or dynamic storage duration is obtained, the bytes comprising the storage for the object have the following initial value:
If no initialization is performed for an object (including subobjects), such a byte retains its initial value until that value is replaced.
|
(since C++26) |
Indeterminate and erroneous(since C++26) values of types std::byte, (since C++17)unsigned char, and char (if char is unsigned) can be propagated as follows:
- If an indeterminate or erroneous(since C++26) value of one of these types is produced by the evaluation of:
- the second or third operand of a conditional expression,
- the right operand of a comma expression,
- the operand of an implicit conversion or explicit conversion to one of these types,
- a discarded-value expression,
- the result of the operation is an indeterminate value or the erroneous value, respectively(since C++26).
- If an indeterminate or erroneous(since C++26) value is assigned to an object of one of these types, the object acquires an indeterminate value or the erroneous value, respectively(since C++26).
- If an indeterminate or erroneous(since C++26) value is used to initialize an object of one of these types, the object acquires an indeterminate value or the erroneous value, respectively(since C++26).
Every other use of an indeterminate value is undefined behavior.
|
Every other use of an erroneous value is erroneous behavior: it may result in compile-time warning or error, run-time error, or an implementation-defined value. Every use of an erroneous value observes the same value. Note that if the observed value would be invalid for its type (for example, if the value is |
(since C++26) |
// Case 1: Uninitialized objects with dynamic storage duration
// All C++ versions: indeterminate value + undefined behavior
int f(bool b)
{
unsigned char* c = new unsigned char;
unsigned char d = *c; // OK, “d” has an indeterminate value
int e = d; // undefined behavior
return b ? d : 0; // undefined behavior if “b” is true
}
// Case 2: Uninitialized objects with automatic storage duration
// until C++26: indeterminate value + undefined behavior
// since C++26: erroneous value + erroneous behavior
int g(bool b)
{
unsigned char c; // “c” has an indeterminate/erroneous value
unsigned char d = c; // no undefined/erroneous behavior,
// but “d” has an indeterminate/erroneous value
assert(c == d); // until C++26: undefined behavior
// since C++26: holds, but both integral promotions have
// erroneous behavior
int e = d; // undefined/erroneous behavior
return b ? d : 0; // undefined/erroneous behavior if “b” is true
}
// Same as case 2
void h()
{
int d1, d2; // “d1” and “d2” have indeterminate/erroneous values
int e1 = d1; // undefined/erroneous behavior
int e2 = d1; // undefined/erroneous behavior
assert(e1 == e2); // holds since C++26
assert(e1 == d1); // holds since C++26, undefined/erroneous behavior
assert(e2 == d1); // holds since C++26, undefined/erroneous behavior
// no undefined/erroneous behavior,
// but “d2” has an indeterminate/erroneous value
std::memcpy(&d2, &d1, sizeof(int));
assert(e1 == d2); // holds since C++26, undefined/erroneous behavior
assert(e2 == d2); // holds since C++26, undefined/erroneous behavior
}
Notes
References and const scalar objects cannot be default-initialized.
| Feature-test macro | Value | Std | Feature |
|---|---|---|---|
__cpp_constexpr |
201907L |
(C++20) | Trivial default-initialization and asm-declaration in constexpr functions
|
Example
#include <string>
struct T1 { int mem; };
struct T2
{
int mem;
T2() {} // “mem” is not in the initializer list
};
int n; // static non-class, a two-phase initialization is done:
// 1) zero-initialization initializes n to zero
// 2) default-initialization does nothing, leaving n being zero
int main()
{
[[maybe_unused]]
int n; // non-class, the value is indeterminate
std::string s; // class, calls default constructor, the value is ""
std::string a[2]; // array, default-initializes the elements, the value is {"", ""}
// int& r; // Error: a reference
// const int n; // Error: a const non-class
// const T1 t1; // Error: const class with implicit default constructor
[[maybe_unused]]
T1 t1; // class, calls implicit default constructor
const T2 t2; // const class, calls the user-provided default constructor
// t2.mem is default-initialized
}
Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
| DR | Applied to | Behavior as published | Correct behavior |
|---|---|---|---|
| CWG 178 | C++98 | there was no value-initialization; empty initializer invoked default-initialization (though new T() also performs zero-initialization)
|
empty initializer invokes value-initialization |
| CWG 253 | C++98 | default-initialization of a const object could not call an implicitly declared default constructor |
allowed if all subobjects are initialized |
| CWG 616 | C++98 | lvalue to rvalue conversion of any uninitialized object was always UB |
indeterminate unsigned char is allowed
|
| CWG 1787 | C++98 | read from an indeterminate unsigned charcached in a register was UB |
made well-defined |
| CWG 3089 | C++11 | std::nullptr_t is not const-default-initializable
|
made const-default-initializable |