Is there a difference between initializing a structure these two ways:
structVar = {}
and
structVar = {0}
It really depends on the nature of the struct.
If it is an aggregate, the two are largely equivalent. However, the first one is general in that it simply value-initializes the struct. The second one requires that the struct's first member be initializable with 0, and initializes the rest of elements as if they were each initialized by an empty initializer {}, in other words,
structVar = {0, {}, {}, {}, ..... };
so each remaining element is value-initialized.
If it is not an aggregate, then it depends on what constructors, if any, have been provided. There simply isn't enough information to say.
corrected
If the structure is an aggregate, the 2 definitions perform the same thing. From the C++11 standard (N3337):
8.5.1/7 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 its brace-or-equal-initializer or, if there is no brace-or-equal- initializer, from an empty initializer list (8.5.4).
If the structure contains two members, like
struct S
{
int x;
double y;
};
then structVar = {} will value-initialize the members, i.e. they will become zero (or if the members are non-PODs, the default ctor will be called).
The line structVar = {0} will initialize only the first member of the struct with zero. Since in this case you have an aggregate, the rest of the members are value-initialized by empty lists, so effectively you have
structVar = {0, {} };
C++14 added the possibility of aggregate initialization for aggregates containing brace-or-equal initialized members, i.e.
struct S
{
int x;
double y{1.1}; // or double y = 1.1;
}
In this case,
structVar = {0};
initializes x with 0 and y with 1.1. This was not possible before C++14.
PS: just tested the code above, clang++ compiles it with -std=c++1y, however g++ 4.9.2 rejects it even if I compile with -std=c++14.
Related
In C++17, consider a case where S is a struct with a deleted default constructor and a float member, when S is initialized with empty braces, is the float member guaranteed by the standard to be zero-initialized?
struct A {
int x{};
};
struct S
{
S() = delete;
A a;
float b;
};
int main()
{
auto s = S{}; // Is s.b guaranteed to be zero?
}
In my opinion, cppreference.com is not clear, saying both that:
If the number of initializer clauses is less than the number of members and basesor initializer list is completely empty, the remaining members and bases (since C++17) are initialized by their default member initializers, if provided in the class definition, and otherwise (since C++14) copy-initialized from 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.
(from here), which implies that b is guaranteed to be zero
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.
(from here)
which implies that b is not guaranteed to be zero.
There is also a discussion that seems to imply that while not guaranteed, all known compiler zero-initialize anyway:
The standard specifies that zero-initialization is not performed when the class has a user-provided or deleted default constructor, even if that default constructor is not selected by overload resolution. All known compilers performs additional zero-initialization if a non-deleted defaulted default constructor is selected.
Related to Why does aggregate initialization not work anymore since C++20 if a constructor is explicitly defaulted or deleted?
This is a quirk of C++ that is fixed in C++20. In the meantime you can add explicit to the deleted default constructor to force the struct to become non-aggregate, and make your code a guaranteed compile error:
struct A {
int x{};
};
struct S
{
explicit S() = delete;
const A a;
const float b;
};
int main()
{
auto s = S{}; // error: call to deleted constructor of 'S'
}
Because S is an aggregate, S{} will perform aggregate initialization. The rule in the standard about how members are initialized when there are no initializers in the list is basically what you cited:
If the element has a default member initializer ([class.mem]), the element is initialized from that initializer.
Otherwise, if the element is not a reference, the element is copy-initialized from an empty initializer list ([dcl.init.list]).
So for b, that's the equivalent of float b = {};. Per the rules of list initialization, we have to get all the way down to 3.10:
Otherwise, if the initializer list has no elements, the object is value-initialized.
And value initialization will initialize a float to 0.
I'm debugging some code that essentially is identical to this:
struct Foo { int a; int b; };
struct Bar { Bar() {} Foo foo{0}; };
When I make an instance of Bar, it seems like both a and b are initialized to zero. Is this guaranteed? Where can I find that in the spec?
According to cppreference.com
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 member 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.
Foo has no default member initializers (int b{0};), so b will be initialized by list-initialization with an empty list, which means value-initialization for non-class types: b = int() // = 0.
In C++ if I initialize the structure in the form of "= {}", as in example below, does it ensure to assign values zero to all the member of the structure?
I understand this seem duplicate question, But my question also is if it initializes zero to all members, does it also apply for complex structure ?
Like structure within structure , or for this each member has to be explicitly assigned value zero in the code?.
typedef struct s{
int i;
bool x;
};
int main ()
{
s initial = {};
printf("%d %d", initial.i, initial.x);
}
Edit: To reference complex structure,
typedef struct scomplex{
s initial;
s t[5];
};
int main (void)
{
scomplex sc = {};
printf ("%d %d %d",sc.initial.i, sc.initial.x, sc.t[0].i);
}
But my question also is if it initializes zero to all members, does it also apply for complex structure ?
Yes, all members will be initialized, including "complex" member, but might not be initialized to zero, the final effect is determined by their types.
According to your sample code, struct s is an aggregate type, then aggregate initialization is performed.
(emphasis mine)
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).
For this case the member i and x of struct s will be value initialized to zero.
4) otherwise, the object is zero-initialized.
If struct s has any other members, they'll be initialized (value initialized or aggregate initialized according to their types) by empty lists recursively.
EDIT
For your added sample (struct scomplex), the member initial will be value initialized, and the final effect depends on the type s. And another member is an array, which will be aggregate initialized with empty list, and all the elements of the array will be value initialized; Same as member initial, the effect depends on the type s.
Problem
Will this initialize all of the members to 0?
typedef struct s{
int i;
bool x;
};
int main ()
{
s initial = {};
printf("%d %d", initial.i, initial.x);
}
Answer: yes. Proof? Here you can see it become 0.
Better Alternatives?
This is an opinionated section. But In My Opinion (IMO), initializing it with {0} would be more readable than {}, as it notifies the user of the 0. It is actually being filled up with 0's.
s initial = {0};
What is this called?
This is called Aggregate Initialization, as Dieter Lücking defined, or Value Initialization, as songyuanyao noted. It's basically a form of initialization where you can initialize a struct with values you would like. For example, let's initialize it with the value 1 instead of 0! You would do:
// Example program
#include <stdio.h>
#include <iostream>
typedef struct s{
int i;
bool x;
};
int main ()
{
s initial = {1,1};
printf("%d %d", initial.i, initial.x);
}
You can see this compiled here. As you can see above, I am doing 1,1 which is normal initialization. As opposed to 0 initialization, you can't just initialize all the parts of the struct as easily as you can with 0.
References
cpprefrence
what is aggregate initialization
What do the following phrases mean in C++: zero-, default- and value-initialization?
Glossary
Aggregate Initialization :
Aggregate initialization is a form of list-initialization, which initializes aggregates.
Value Initialization:
Initialize values
This is the initialization performed when a variable is constructed with an empty initializer.
I am using a C-style structure which does not have any constructor, like this:
struct structName {
int mem1;
int mem2;
char mem3;
char mem4;
}
I am creating a variable of this structure and I want to initialize all members of the structure to zero. I found the following methods.
struct structName structVar = {};
struct structName structVar = {0};
struct structName structVar = struct structName();
For the first two methods, my compiler is giving "missing initializer for member" warning.
The third approach compiles without warnings.
Is it a valid C++ statement?
Am I missing some genuine warning/error by using method 3?
Is there any better alternative method, apart from memset()?
The preferred method should be one of:
structName structVar{};
structName structVar = {};
auto structName = structVar{};
there are subtle differences, but not for aggregates as in your example
This has the added advantage that it initializes structVar for any type of structName or if it cannot perform an initialization it makes the program ill-formed (the code doesn't compile) (plus it doesn't allow narrowing).
In your specific example, structName is an agregate:
C++14 draft standard:
§8.5.1 Aggregates [dcl.init.aggr]
(1) An aggregate is an array or a class (Clause 9) with no user-provided
constructors (12.1), no private or protected non-static data members
(Clause 11), no base classes (Clause 10), and no virtual functions
(10.3)
The initialization syntax I used is called List-initialization:
§8.5.4 List-initialization [dcl.init.list]
(1) List-initialization is
initialization of an object or reference from a braced-init-list.
[...] An initializer list may be empty. [...]
For our aggregate this means:
§8.5.1 Aggregates [dcl.init.aggr]
(2) When an aggregate is initialized by an initializer list, as
specified in 8.5.4, the elements of the initializer list are taken as
initializers for the members of the aggregate, in increasing subscript
or member order. Each member is copy-initialized from the
corresponding initializer-clause
(7) 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 its brace-or-equal-initializer or, if there
is no brace-or-equalinitializer, from an empty initializer list
(8.5.4).
[ Example:
struct S { int a; const char* b; int c; int d = b[a]; };
S ss = { 1, "asdf" };
initializes ss.a with 1, ss.b with "asdf", ss.c with the value of an
expression of the form int{} (that is, 0), and ss.d with the value
of ss.b[ss.a] (that is, ’s’)
[...]
end example ]
So all of these are valid and do the exact same thing:
structName structVar = {};
structName structVar = {0};
structName structVar = {0, 0};
However if there is at least one initializer-clauses and less than there are members in the aggregate, gcc and clang emit a warning. It might be that you intended to initialize all members, but missed some. So the empty initializer list is the safest choice.
As a side note struct is not needed and universally not used in a declaration. So replace this:
struct structName structVar
with:
structName structVar
Suppose I have the following struct:
struct sampleData
{
int x;
int y;
};
And when used, I want to initialize variables of sampleData type to a known state.
sampleData sample = { 1, 2 }
Later, I decide that I need additional data stored in my sampleData struct, as follows:
struct sampleData
{
int x;
int y;
int z;
};
It is my understanding that the two field initialization left over from my pre-z data structure is still a valid statement, and will be compiled., populating the missing fields with default values.
Is this understanding correct? I have been working recently in Ada, which also allows aggregate initialization, but which would flag a similar issue as a compilation error. Assuming that my assumptions about the C++ code above are correct, is there a language construct which would recognize missing initialization values as an error?
Initialising variables that way is only supported with Aggregate Classes.
If you add constructor(s) then then problem goes away, but you'll need to change the syntax a little and you lose the ability to store the struct in a union (among other things).
struct sampleData
{
sampleData(int x, int y) : x(x), y(y) {}
int x;
int y;
};
sampleData sample( 1, 2 );
Adding z (and changing the constructor) will mark sample( 1, 2 ) as a compile error.
Yes, any elements you leave off of the initialization list will be initialized to zero (for POD scalar types) or using their default constructor (for classes).
The relevant language from the C standard is quoted here:
[6.7.8.21] If there are fewer initializers in a brace-enclosed list than there are elements or members of an aggregate, or fewer characters in a string literal used to initialize an array of known size than there are elements in the array, the remainder of the aggregate shall be initialized implicitly the same as objects that have static storage duration.
I am sure someone more motivated than I could find the corresponding language in one of the C++ specs...
Note that this implies that POD scalar elements are initialized as if you wrote "= 0". Which means it will correctly initialize pointers to NULL and floats to 0.0 even if their representations do not happen to be all-zero bytes. It also implies that it works recursively; if your struct contains a struct, the inner struct will be properly initialized as well.
As a followup to Nemo's answer with the C standardese, here is what the C++03 standard says:
§8.5.1/7:
If there are fewer initializers in the list than there are members in the aggregate, then each member not explicitly initialized shall be value-initialized.
§8.5/5:
To value-initialize an object of type T means:
if T is a class type with a user-declared constructor, 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
To zero-initialize an object of type T means:
if T is a scalar type, 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) is zero-initialized;
if T is an array type, each element is zero-initialized;
if T is a reference type, no initialization is performed.
Why not use
sampleData sample = { x: 1, y:2 } ;
?
But you'd still run into the problem of z being initialized to an unpredictable value, so it's better to define a constructor which sets all variables to well defined values.