Initializers
The latest version of this topic can be found at Initializers.
An initializer specifies the initial value of a variable. You can initialize variables in these contexts:
In the definition of a variable:
int i = 3; Point p1{ 1, 2 };
As one of the parameters of a function:
set_point(Point{ 5, 6 });
As the return value of a function:
Point get_new_point(int x, int y) { return { x, y }; } Point get_new_point(int x, int y) { return Point{ x, y }; }
Initializers may take these forms:
An expression (or a comma-separated list of expressions) in parentheses:
Point p1(1, 2);
An equals sign followed by an expression:
string s = "hello";
A braced initializer list. The list may be empty or may consist of a set of lists, as in the following example:
struct Point{ int x; int y; }; class PointConsumer{ public: void set_point(Point p){}; void set_points(initializer_list<Point> my_list){}; }; int main() { PointConsumer pc{}; pc.set_point({}); pc.set_point({ 3, 4 }); pc.set_points({ { 3, 4 }, { 5, 6 } }); }
Kinds of initialization
There are several kinds of initialization, which may occur at different points in program execution. Different kinds of initialization are not mutually exclusive—for example, list initialization can trigger value initialization and in other circumstances, it can trigger aggregate initialization.
Zero initialization
Zero initialization is the setting of a variable to a zero value implicitly converted to the type:
Numeric variables are initialized to 0 (or 0.0, or 0.0000000000, etc.).
Char variables are initialized to ‘\0’.
Pointers are initialized to
nullptr
.Arrays, POD classes, structs, and unions have their members initialized to a zero value.
Zero initialization is performed at different times:
At program startup, for all named variables that have static duration. These variables may later be initialized again.
During value initialization, for scalar types and POD class types that are initialized by using empty braces.
For arrays that have only a subset of their members initialized.
Here are some examples of zero initialization:
struct my_struct{
int i;
char c;
};
int i0; // zero-initialized to 0
int main() {
static float f1; // zero-initialized to 0.000000000
double d{}; // zero-initialized to 0.00000000000000000
int* ptr{}; // initialized to nullptr
char s_array[3]{'a', 'b'}; // the third char is initialized to '\0'
int int_array[5] = { 8, 9, 10 }; // the fourth and fifth ints are initialized to 0
my_struct a_struct{}; // i = 0, c = '\0'
}
Default initialization
Default initialization for classes, structs, and unions is initialization with a default constructor. The default constructor can be called with no initialization expression or with the new
keyword:
MyClass mc1;
MyClass* mc3 = new MyClass;
If the class, struct, or union does not have a default constructor, the compiler emits an error.
Scalar variables are default initialized when they are defined with no initialization expression. They have indeterminate values.
int i1;
float f;
char c;
Arrays are default initialized when they are defined with no initialization expression. When an array is default-initialized, its members are default initialized and have indeterminate values, as in the following example:
int int_arr[3];
If the array members do not have a default constructor, the compiler emits an error.
Default initialization of constant variables
Constant variables must be declared together with an initializer. If they are scalar types they cause a compiler error, and if they are class types that have a default constructor they cause a warning:
class MyClass{};
int main() {
//const int i2; // compiler error C2734: const object must be initialized if not extern
//const char c2; // same error
const MyClass mc1; // compiler error C4269: 'const automatic data initialized with compiler generated default constructor produces unreliable results
}
Default initialization of static variables
Static variables that are declared with no initializer are initialized to 0 (implicitly converted to the type).
class MyClass {
private:
int m_int;
char m_char;
};
int main() {
static int int1; // 0
static char char1; // '\0'
static bool bool1; // false
static MyClass mc1; // {0, '\0'}
}
For more information about initialization of global static objects, see Additional Startup Considerations.
Value initialization
Value initialization occurs in the following cases:
a named value is initialized using empty brace initialization
an anonymous temporary object is initialized using empty parentheses or braces
an object is initialized with the
new
keyword plus empty parentheses or braces
Value initialization does the following:
for classes with at least one public constructor, the default constructor is called
for non-union classes with no declared constructors, the object is zero-initialized and the default constructor is called
for arrays, every element is value-initialized
in all other cases, the variable is zero initialized
class BaseClass {
private:
int m_int;
};
int main() {
BaseClass bc{}; // class is initialized
BaseClass* bc2 = new BaseClass(); // class is initialized, m_int value is 0
int int_arr[3]{}; // value of all members is 0
int a{}; // value of a is 0
double b{}; // value of b is 0.00000000000000000
}
Copy initialization
Copy initialization is the initialization of one object using a different object. It occurs in the following cases:
a variable is initialized using an equals sign
an argument is passed to a function
an object is returned from a function
an exception is thrown or caught
a non-static data member is initialized using an equals sign
class, struct, and union members are initialized by copy initialization during aggregate initialization. See Aggregate initialization for examples.
The following code shows several examples of copy initialization:
#include <iostream>
using namespace std;
class MyClass{
public:
MyClass(int myInt) {}
void set_int(int myInt) { m_int = myInt; }
int get_int() const { return m_int; }
private:
int m_int = 7; // copy initialization of m_int
};
class MyException : public exception{};
int main() {
int i = 5; // copy initialization of i
MyClass mc1{ i };
MyClass mc2 = mc1; // copy initialization of mc2 from mc1
MyClass mc1.set_int(i); // copy initialization of parameter from i
int i2 = mc2.get_int(); // copy initialization of i2 from return value of get_int()
try{
throw MyException();
}
catch (MyException ex){ // copy initialization of ex
cout << ex.what();
}
}
Copy initialization cannot invoke explicit constructors.
vector<int> v = 10; // the constructor is explicit; compiler error C2440: cannot convert from 'int' to 'std::vector<int,std::allocator<_Ty>>'
regex r = "a.*b"; // the constructor is explicit; same error
shared_ptr<int> sp = new int(1729); // the constructor is explicit; same error
In some cases, if the copy constructor of the class is deleted or inaccessible, copy initialization causes a compiler error. See (NOTINBUILD) Explicit Initialization for more information.
Direct initialization
Direct initialization is initialization using (non-empty) braces or parentheses. Unlike copy initialization, it can invoke explicit constructors. It occurs in the following cases:
a variable is initialized with non-empty braces or parentheses
a variable is initialized with the
new
keyword plus non-empty braces or parenthesesa variable is initialized with
static_cast
in a constructor, base classes and non-static members are initialized with an initializer list
in the copy of a captured variable inside a lambda expression
The following code shows some examples of direct initialization:
class BaseClass{
public:
BaseClass(int n) :m_int(n){} // m_int is direct initialized
private:
int m_int;
};
class DerivedClass : public BaseClass{
public:
// BaseClass and m_char are direct initialized
DerivedClass(int n, char c) : BaseClass(n), m_char(c) {}
private:
char m_char;
};
int main(){
BaseClass bc1(5);
DerivedClass dc1{ 1, 'c' };
BaseClass* bc2 = new BaseClass(7);
BaseClass bc3 = static_cast<BaseClass>(dc1);
int a = 1;
function<int()> func = [a](){ return a + 1; }; // a is direct initialized
int n = func();
}
List initialization
List initialization occurs when a variable is initialized using a braced initializer list. Braced initializer lists can be used in the following cases:
a variable is initialized
a class is initialized with the
new
keywordan object is returned from a function
an argument passed to a function
one of the arguments in a direct initialization
in a non-static data member initializer
in a constructor initializer list
The following code shows some examples of list initialization:
class MyClass {
public:
MyClass(int myInt, char myChar) {}
private:
int m_int[]{ 3 };
char m_char;
};
class MyClassConsumer{
public:
void set_class(MyClass c) {}
MyClass get_class() { return MyClass{ 0, '\0' }; }
};
struct MyStruct{
int my_int;
char my_char;
MyClass my_class;
};
int main() {
MyClass mc1{ 1, 'a' };
MyClass* mc2 = new MyClass{ 2, 'b' };
MyClass mc3 = { 3, 'c' };
MyClassConsumer mcc;
mcc.set_class(MyClass{ 3, 'c' });
mcc.set_class({ 4, 'd' });
MyStruct ms1{ 1, 'a', { 2, 'b' } };
}
Aggregate initialization
Aggregate initialization is a form of list initialization for arrays or class types (often structs or unions) that have:
no private or protected members
no user-provided constructors, except for explicitly defaulted or deleted constructors
no base classes
no virtual member functions
no brace-or-equal initializers for non-static members
Aggregate initializers consist of a braced initialization list, with or without an equals sign, as in the following example:
#include <iostream>
using namespace std;
struct MyAggregate{
int myInt;
char myChar;
};
int main() {
MyAggregate agg1{ 1, 'c' };
cout << "agg1: " << agg1.myChar << ": " << agg1.myInt << endl;
cout << "agg2: " << agg2.myChar << ": " << agg2.myInt << endl;
int myArr1[]{ 1, 2, 3, 4 };
int myArr2[3] = { 5, 6, 7 };
int myArr3[5] = { 8, 9, 10 };
cout << "myArr1: ";
for (int i : myArr1){
cout << i << " ";
}
cout << endl;
cout << "myArr3: ";
for (auto const &i : myArr3) {
cout << i << " ";
}
cout << endl;
}
You should see the following output:
agg1: c: 1
agg2: d: 2
myArr1: 1 2 3 4
myArr3: 8 9 10 0 0
Important
Array members that declared but not explicitly initialized during aggregate initialization are zero-initialized, as in myArr3
above.
Initializing unions and structs
If a union does not have a constructor, you can initialize it with a single value (or with another instance of a union). The value is used to initialize the first non-static field. This is different from struct initialization, in which the first value in the initializer is used to initialize the first field, the second to initialize the second field, and so on. Compare the initialization of unions and structs in the following example:
struct MyStruct {
int myInt;
char myChar;
};
union MyUnion {
int my_int;
char my_char;
bool my_bool;
MyStruct my_struct;
};
int main() {
MyUnion mu1{ 'a' }; // my_int = 97, my_char = 'a', my_bool = true, {myInt = 97, myChar = '\0'}
MyUnion mu2{ 1 }; // my_int = 1, my_char = 'x1', my_bool = true, {myInt = 1, myChar = '\0'}
MyUnion mu3{}; // my_int = 0, my_char = '\0', my_bool = false, {myInt = 0, myChar = '\0'}
MyUnion mu4 = mu3; // my_int = 0, my_char = '\0', my_bool = false, {myInt = 0, myChar = '\0'}
//MyUnion mu5{ 1, 'a', true }; // compiler error: C2078: too many initializers
//MyUnion mu6 = 'a'; // compiler error: C2440: cannot convert from 'char' to 'MyUnion'
//MyUnion mu7 = 1; // compiler error: C2440: cannot convert from 'int' to 'MyUnion'
MyStruct ms1{ 'a' }; // myInt = 97, myChar = '\0'
MyStruct ms2{ 1 }; // myInt = 1, myChar = '\0'
MyStruct ms3{}; // myInt = 0, myChar = '\0'
MyStruct ms4{1, 'a'}; // myInt = 1, myChar = 'a'
MyStruct ms5 = { 2, 'b' }; // myInt = 2, myChar = 'b'
}
Initializing aggregates that contain aggregates
Aggregate types can contain other aggregate types, for example arrays of arrays, arrays of structs, and so on. These types are initialized by using nested sets of braces, for example:
struct MyStruct {
int myInt;
char myChar;
};
int main() {
int intArr1[2][2]{{ 1, 2 }, { 3, 4 }};
int intArr3[2][2] = {1, 2, 3, 4};
MyStruct structArr[]{ { 1, 'a' }, { 2, 'b' }, {3, 'c'} };
}
Reference initialization
Variables of reference type must be initialized with an object of the type from which the reference type is derived, or with an object of a type that can be converted to the type from which the reference type is derived. For example:
// initializing_references.cppint
int iVar;
long lVar;
int main()
{ long& LongRef1 = lVar; // No conversion required.
long& LongRef2 = iVar; // C2440
const long& LongRef3 = iVar; // OK
LongRef1 = 23L; // Change lVar through a reference.
LongRef2 = 11L; // Change iVar through a reference.
LongRef3 = 11L; // C3892}
The only way to initialize a reference with a temporary object is to initialize a constant temporary object. Once initialized, a reference-type variable always points to the same object; it cannot be modified to point to another object.
Although the syntax can be the same, initialization of reference-type variables and assignment to reference-type variables are semantically different. In the preceding example, the assignments that change iVar
and lVar
look similar to the initializations, but have different effects. The initialization specifies the object to which the reference-type variable points; the assignment assigns to the referred-to object through the reference.
Because both passing an argument of reference type to a function and returning a value of reference type from a function are initializations, the formal arguments to a function are initialized correctly, as are the references returned.
Reference-type variables can be declared without initializers only in the following:
Function declarations (prototypes). For example:
int func ( int& );
Function-return type declarations. For example:
int& func ( int& );
Declaration of a reference-type class member. For example:
class c {public: int& i;};
Declaration of a variable explicitly specified as
extern
. For example:extern int& iVal;
When initializing a reference-type variable, the compiler uses the decision graph shown in the following figure to select between creating a reference to an object or creating a temporary object to which the reference points.
Decision Graph for Initialization of Reference Types
References to volatile
types (declared as volatile
typename**&** identifier) can be initialized with volatile
objects of the same type or with objects that have not been declared as volatile
. They cannot, however, be initialized with const objects of that type. Similarly, references to const types (declared as const typename**&** identifier) can be initialized with const objects of the same type (or anything that has a conversion to that type or with objects that have not been declared as const). They cannot, however, be initialized with volatile
objects of that type.
References that are not qualified with either the const or volatile
keyword can be initialized only with objects declared as neither const nor volatile
.
Initialization of external variables
Declarations of automatic, register, static, and external variables can contain initializers. However, declarations of external variables can contain initializers only if the variables are not declared as extern
. For more information, see External.