Initializing member variables of a struct in c++ - c++

I have a struct with a few double values:
struct A {
double a;
double b;
}
if I create a new struct, e.g. A a, are all the members (e.g. a.a) initialized to zeroes automatically in C++?

Not by default (unless it's a variable of static storage - that is, a static or global variable).
There are a few ways to initialize a struct of this kind to "zeros":
A a = { 0.0, 0.0 };
A a = { };
A a = A();
or if you have a C++11 compatible compiler:
A a{0.0, 0.0};
A a{}
or add a constructor to the struct definition:
struct A {
double a;
double b;
A() : a(0.0), b(0.0) {}
};

8.5. Initializers [dcl.init] / 11.
If no initializer is specified for an object, the object is default-initialized; if no initialization is performed, an
object with automatic or dynamic storage duration has indeterminate value. [ Note: Objects with static or
thread storage duration are zero-initialized, see 3.6.2. — end note ]
and (ordering reversed for readability):
8.5. Initializers [dcl.init] / 6.
To default-initialize an object of type T means:
— if T is a (possibly cv-qualified) 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. [emphasis mine]
If a program calls for the default initialization of an object of a const-qualified type T, T shall be a class type
with a user-provided default constructor.
They are default initialized. For builtin types like int or double, their value depends on where the struct is declared (as a rule of thumb (but just as that): Assume they are always garbage unless initialized).
In global scope or/and with static storage, they are all zeroes (incl. when the struct is a member of a struct which is at global scope).
At function-local scope, they are full of garbage.
Example:
#include <iostream>
struct Foo {
int x;
int y;
};
Foo foo;
int main () {
Foo bar;
std::cout << foo.x << ":" << foo.y << '\n';
std::cout << bar.x << ":" << bar.y << '\n';
}
This on the first run gives me
0:0
-1077978680:12574708
On the second run, without recompilation, this gives me:
0:0
-1075556168:12574708
A POD-struct can be initialized all zeroes using e.g. memset or just ...
Foo foo = {0}; // C and C++03
Foo foo{0}; // C++11

No. In the general case, they have unspecified values.
If you don't like this behaviour, you can provide a constructor:
struct A {
double a;
double b;
A(): a(0.0), b(0.0) {}
}

Related

Value initialization of nested classes

By the rules of value initialization. Value initialization occurs:
1,5) when a nameless temporary object is created with the initializer
consisting of an empty pair of parentheses or braces (since C++11);
2,6) when an object with dynamic storage duration is created by a
new-expression with the initializer consisting of an empty pair of
parentheses or braces (since C++11);
3,7) when a non-static data
member or a base class is initialized using a member initializer with
an empty pair of parentheses or braces (since C++11);
4) when a named
variable (automatic, static, or thread-local) is declared with the
initializer consisting of a pair of braces.
Trivial example
struct A{
int i;
string s;
A(){};
};
A a{}
cout << a.i << endl // default initialized value
without explicitly declared constructor and left with defaulted default ctor // compiler generated one we get.
struct A{
int i;
string s;
};
A a{};
cout << a.i << endl // zero-initialized value
However using antoher struct.
struct A{
int i;
string s;
};
struct B{
A a;
int c;
};
B a{};
cout << a.a.i << endl // default initialized , even tho we did not , int struct B , declared A a{}.
The value of a.i is zero-initialized, even without using {} / () construct, which goes against rules ( if i am not mistaken ).
Using same logic on struct B:
struct A{
int i;
string s;
};
struct B{
A a;
int c;
};
B b;
cout << b.c << endl; // default inicialized
We get behavior according to rules.
The last example:
struct A
{
int i;
A() { }
};
struct B { A a; };
std::cout << B().a.i << endl;
B().a.i is also zero-initialized while we explicitly declared constructor and its not deleted.
Why are the these values getting zero-initialized? By rules stated here they should be default-initialized not zero-initialized.
Thanks for answers.
It's the difference between A being an aggregate or not.
[dcl.init.aggr] (Emphasis mine)
An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no brace-or-equal initializers
for non-static data members (9.2), no private or protected non-static data members (Clause 11),
So when A has no declared constructors, saying A a{} has the effect of aggregate initialization
which will construct each member with an empty initialization list:
If there are fewer initializer-clauses in the list than there are members in the aggregate, then each member
not explicitly initialized shall be initialized from an empty initializer list
So you get int{} and std::string{} which will value-initialize the integer member to zero.
When you do provide a default constructor, the aggregate property is lost and the int member remains uninitialized, so accessing it is considered undefined behavior.
To be specific:
This code is undefined behavior when accessing a.i because you provided a user-defined constructor, so the int i field remains uninitialized after construction:
struct A{
int i;
string s;
A(){};
};
A a{} ;
cout << a.i << endl;
And this code exhibits undefined behavior when accessing b.c because you did not perform list initialization on B b:
struct B{
A a;
int c;
};
B b;
cout << b.c << endl;
All the other code is okay, and will zero-initialize the integer fields. In the scenarios where you are using braces {}, you are performing aggregate-initialization.
The last example is a little tricky because you're performing value-initialization. Since B is an aggregate, it gets zero-initialized ([dcl.init]), in which:
each base-class
subobject is zero-initialized
So again you're okay accessing the integer member of the A subobject.
According to the rules of aggregate initialization, the members are indeed value initialized.
Value initialization:
In all cases, if the empty pair of braces {} is used and T is an aggregate type, aggregate-initialization is performed instead of value-initialization.
The effects of aggregate initialization are:
If the number of initializer clauses is less than the number of members and bases (since C++17) or initializer list is completely empty, the remaining members and bases (since C++17) are initialized by their default initializers, if provided in the class definition, and otherwise (since C++14) by empty lists, in accordance with the usual list-initialization rules (which performs value-initialization for non-class types and non-aggregate classes with default constructors, and aggregate initialization for aggregates). If a member of a reference type is one of these remaining members, the program is ill-formed.

Initialization of a static member inside a template

Here's a minimal example:
#include <iostream>
struct B {
B() { x = 42; }
static int x;
};
int B::x;
template <int N>
struct A {
int foo() { return b.x; }
static B b;
};
template<int N>
B A<N>::b;
//template struct A<2>; // explicit instantiation with N = 2 (!)
int main(int argc, char **argv) {
std::cout << A<1>().foo() << std::endl;
return 0;
}
This program writes 42 using g++ 4.9.2, but writes 0 using Visual Studio 2015 RC. Also, if I uncomment the explicit instantiation, VS2015RC also gives 42, which is quite interesting, as the template parameter here is different from the one used in the main function.
Is this a bug? I assume that g++ is correct, as there is a reference to b inside foo, so B's constructor should be called.
EDIT: There is a simple workaround - if there is a non-static variable in B, that is referenced in A, VS2015RC will compile correctly:
// ...
struct B {
B() { x = 42; }
static int x;
int y; // <- non-static variable
};
// ...
template <int N>
struct A {
int foo() { b.y; return b.x; } // <- reference to b.y
static B b;
};
This seems to work, even though b.y, as a statement, is obviously NOP.
From [basic.start.init]:
Variables with static storage duration (3.7.1) or thread storage duration (3.7.2) shall be zero-initialized (8.5)
before any other initialization takes place. A constant initializer for an object o is an expression that is a
constant expression, except that it may also invoke constexpr constructors for o and its subobjects even
if those objects are of non-literal class types. [ ... ]
Together, zero-initialization and constant initialization are called static initialization; all other initialization is
dynamic initialization. Static initialization shall be performed before any dynamic initialization takes place.
In our case, b is statically initialized but b.x is dynamically initialized (the constructor isn't constexpr). But we also have:
It is implementation-defined whether the dynamic initialization of a non-local variable with static storage
duration is done before the first statement of main. If the initialization is deferred to some point in time
after the first statement of main, it shall occur before the first odr-use (3.2) of any function or variable
defined in the same translation unit as the variable to be initialized.
Odr-used means, from [basic.def.odr]:
A variable x whose name appears as a potentially-evaluated expression ex is odr-used by ex unless applying
the lvalue-to-rvalue conversion (4.1) to x yields a constant expression (5.20) that does not invoke any nontrivial
functions and, if [ ... ]
But evaluating b.x does not yield a constant expression, so we can stop there - b.x is odr-used by A<N>::foo(), which is also the first odr-use. So while the initialization does not have to occur before main(), it does have to occur before foo(). So if you get 0, that's a compiler error.
I would be inclined to write the code like this:
struct B {
B() {}
static int x;
};
int B::x = 42;
After all, the static (x) is defined (and therefore should be initialized) on the last line. Putting the initialization inside the constructor of B means that the static x (there is only one of them!) would be re-initialized each and every single time you construct a B. There is one static, you should initialize only once.

Why doesn't VS2013 zero-initialize this struct? [duplicate]

This question already has answers here:
Value initialization and Non POD types
(2 answers)
Closed 8 years ago.
Using Microsoft Visual Studio Ultimate 2013 update 4 and the following code:
#include <iostream>
auto main() -> int {
struct Base { int a; };
struct Derived : Base { int b; };
auto foo = Derived();
std::cout << foo.a << " " << foo.b;
}
I get this output:
-858993460 -858993460
But I expected:
0 0
Why is foo not zero-initialized?
Should be initialized to zero, looks like MSVC bug.
Since actually your code is like this
Derived foo{Derived()};
then copy/move constructor will be called on temporary object initialized with ().
n3376 8.5/10
An object whose initializer is an empty set of parentheses, i.e., (),
shall be value-initialized.
n3376 8.5/7
To value-initialize an object of type T means:
if T is a (possibly cv-qualified) non-union class type without a
user-provided or deleted default construc- tor, then the object is
zero-initialized and, if T has a non-trivial default constructor,
default-initialized;
n3376 8.5/5
To zero-initialize an object or reference of type T means:
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;
Your structs have no constructors, so default one are created.
Default constructor of a struct will call default constructor for each of memebers.
Default constructor of int doesn't initialize it. So int memeber will contain random data.
Add constructors to your structs and initialize memebers. Like this:
struct Base
{
Base(): a(0) {}
int a;
};

Template function that returns default constructed value

I have a function that returns default constructed value of template type:
template<typename T>
T do_stuff()
{
return T();
}
I use it like this:
int main(int argc, char** argv)
{
std::string str("hello");
int a = 10;
int *p = &a;
str = do_stuff<std::string>();
a = do_stuff<int>();
p = do_stuff<int*>();
return 0;
}
After I use it I have: str is an empty string, a eqauls 0 and p is a null pointer.
I can understand why std::string variable becomes an empty string (it has default constructor that constructs an empty string).
But why an int variable becomes 0 and a pointer becomes a null pointer.
Is it default template behaviour?
I use gcc 4.6.6 under Linux Centos.
From this reference on value initialization:
If T is a class type with at least one user-provided constructor of any kind, the default constructor is called.
If T is an non-union class type without any user-provided constructors, then the object is zero-initialized and then the implicitly-declared default constructor is called (unless it's trivial)
If T is an array type, each element of the array is value-initialized
Otherwise, the object is zero-initialized.
What is happening is that the last point in the above list.
The key is in
//------vv
return T();
For example, you can test the following, which is equivalent:
int x = int();
std::cout << x;
x will always be 0 in this case. The same is applied for the pointer - it's zero-initialized, "making" it NULL.
This is value initialization, "caused" by the parenthesis.
Because T() is value initialization, which for int and pointer types (and other basic types) is zero-initialization.
Just like to value initialize an int you'd do:
int x{};
int x = int();
//not initialized:
int x; //if not at namespace scope
int x(); //method declaration
This is as defined in c++ 11 standard, section 8.5:
To value-initialize an object of type T means:
— if T is a class type (clause 9) with a user-declared 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 non-union class type without a user-declared constructor, then every non-static data member and base-class component of T is value-initialized;
— if T is an array type, then each element is value-initialized;
— otherwise, the object is zero-initialized
And:
To default-initialize an object of type T means:
— if T is a 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, the object is zero-initialized.
And:
To zero-initialize an object of type T means:
— if T is a scalar type (3.9), the object is set to the value of 0 (zero) converted to T;
— if T is a non-union class type, each nonstatic data member and each base-class subobject is zero-initialized;
— if T is a union type, the object’s first named data member 89) is zero-initialized;
— if T is an array type, each element is zero-initialized;
— if T is a reference type, no initialization is performed.
That is correct behaviour for the respective types, everywhere.
Primitive types are initialized differently depending on whether you explicitly request the default value (value initialization) or don't mention the initialization (default initialization).
If you construct primitive type without mentioning initialization (this is called default initialization), the value is random:
int x;
struct Y {
int x;
} x;
int *x = new int;
are all default initializations and will contain random value.
But if you mention the initializer, it becomes value initialization and the value is initialized to appropriate "zero" (0 for numbers, 0/nullptr for pointers):
int x = 0;
int x = int();
struct Y {
int x;
Y() : x() {} // the x() is important
} y;
struct Z {
int x;
} z = {};
int *x = new int();
are all value initializations and C++11 adds the following forms
int x{};
struct Y {
int x;
Y() : x{} {}
} y;
struct Z {
int x;
} z{};
int *x = new int{};
Beware of
int x(); // function, NOT A VARIABLE
it declares a function taking no arguments and returning int. This is called the "most vexing parse".
The T() is value initialization.
Use:
int x{};
int x = int();

When are C++ implicit types initialized to 0?

I grew some doubts after discussing this with colleagues...
As the title asks, when can it be assumed that built-in types will be initialized to 0 instead of an unknown value?
Do the rules vary among the c++ standards?
The full rules are in [dcl.init] (C++11). To summarize: when no initialiser is provided in a declaration, the entity is so-called default-initialised. For class types, this means the default constructor is called. For non-class types, this means no initialisation is performed.
However, [dcl.init] §9 states: "Every object of static storage duration is zero-initialized at program startup before any other initialization takes place."
This means that static-duration variables (such as namespace-scope variables) of non-class type are zero-initialised. Other objects of non-class types (such as local variables) are not initialised.
Rather than answering the exact question you post, which has been answered before: only POD objects with static storage will be initialized to 0 automatically, I will try to provide pieces of code to get the compiler to initialize the members for you:
struct POD {
int a;
double b; //...
};
// namespace level:
POD p;
void f() {
POD n1; // uninitialized
POD p1 = {};
POD p2 = POD();
POD* n2 = new POD; // uninitialized
POD* pp1 = new POD();
delete n2; delete pp1;
}
In the examples above only those marked with 'uninitialized' won't be initiazed. Note that this is with respect to what the standard mandates, your mileage will vary with different compilers. In particular VS has some issues with T t = T(); and T* p = new T()' in some scenarios (IIRC when the type T is not a POD, but does not have a user provided default constructor the compiler will fail to initialize the POD subobjects:
struct T {
std::string s;
int i;
};
void f() {
T t = T(); // t.i == 0 according to the standard
}
Theory:
According to both C++98 and C++03 standards:
3.6.2 Initialization of non-local objects, §1:
Objects with static storage duration (3.7.1) shall be zero-initialized (8.5) before any other initialization takes place.
3.7.1 Static storage duration, §1:
All objects which neither have dynamic storage duration nor are local have static storage duration.
3.7.1 Static storage duration, §3:
The keyword static can be used to declare a local variable with static storage duration.
And also 8.5 Initializers, §6:
Every object of static storage duration shall be zero-initialized at program startup before any other initialization takes place.
This is the same in both standards. The only difference is in formulation of C++98's 8.5 §6:
The memory occupied by any object of static storage duration shall be zero-initialized
at program startup before any other initialization takes place.
Example:
Here's the example, where x and y and have static storage duration, thus standard guarantees that both of them will be zero-initialized at program startup. Note that there are also POD objects a and b declared in the same manner, thus having static storage duration, which means that their members (i and d) will be zero-initialized as well:
struct POD {
int i;
double d;
};
int x;
POD a;
int foo() {
static int y;
return y;
}
int main() {
static POD b;
std::cout << "x = " << x << std::endl;
std::cout << "y = " << foo() << std::endl;
std::cout << "a.i = " << a.i << std::endl;
std::cout << "b.d = " << b.d << std::endl;
}
Output of this example is then of course:
x = 0
y = 0
a.i = 0
b.d = 0
I would not assume that any implicit type will be initialized to 0. You may find this is the case when you are running inside the debugger and using the debug heap/stack. When you are outside of the debugger or disable the debug heap via _NO_DEBUG_HEAP=1 environment variable or otherwise you will find that memory is not initialized in most cases.
As a rule of thumb initialize your variables as it is safer to program this way.
EDIT: As pointed out by Luchian Grigore global/namespace scope variables are an exception. They also often don't work with uninitialized variable checks due to this initialization.