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
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 don't understand what happens regarding the zero initialization of structs that has default values for its members.
If I have these structs:
struct A {
int *a;
int b;
};
struct B {
int *a;
int b;
B() : b(3) {}
};
struct C {
int *a;
int b = 3;
};
What we can say without a doubt is:
A a; leaves all fields uninitialized
A a{}; is {nullptr, 0}
B b; and B b{}; both are {garbage, 3} (the constructor is called)
Now it's unclear what happens when I do the following, here are the results using gcc:
C c; // {garbage, 3}
C c{}; // {nullptr, 3}
The question is: does C c{}; guarantees that C::a is initialized to nullptr, in other words, does having default members like in C still zero initialize the other members if I explicitly construct the object like C c{};?
Because it's not what happens if I have a constructor that does the same thing than C (like in B), the other members are not zero initialized, but why? What is the difference between B and C?
As of C++14, C is an aggregate (like A), and C c{} syntax performs aggregate initialization. This includes, in part:
[dcl.init.aggr]/8 If there are fewer initializer-clauses in the list than there are elements in a non-union aggregate, then each element not explicitly initialized is initialized as follows:
(8.1) — If the element has a default member initializer (12.2), the element is initialized from that initializer.
(8.2) — Otherwise, if the element is not a reference, the element is copy-initialized from an empty initializer list (11.6.4).
(8.3) — Otherwise, the program is ill-formed.
So C c{}; is equivalent to C c{{}, 3};. Initializing an int* member with empty list causes it to be zero-initialized.
In C++11, C is not an aggregate (having a default member initializer was disqualifying), and C c{}; calls an implicitly-defined constructor that leaves c.a member uninitialized.
In all versions of the standard, B is not an aggregate due to the user-defined constructor. B b{}; calls that constructor, which explicitly initializes b member and chooses to leave a uninitialized.
Aggregate initialization - cppreference.com
If the number of initializer clauses is less than the number of members or initializer list is completely empty, the remaining members are value-initialized. If a member of a reference type is one of these remaining members, the program is ill-formed.
(until C++11)
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) 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.
(since C++11)
So A a{}; all members are default initialized
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.
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.
If I have a common linux struct like:
struct sockaddr_in
{
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int)
- sizeof(unsigned short int) - sizeof(struct in_addr)];
};
#define sin_zero __pad
and I perform aggregate initialization:
struct sockaddr_in my_addr = { 0 };
how come that this initializes every member to 0?
I mean: documentation says:
If the number of initializer clauses is less than the number of
members or initializer clauses is completely empty, the remaining
members are initialized by their brace-or-equal initializers, if
provided in the class definition, and otherwise (since C++14) by empty
lists, which performs value-initialization.
to make it easy: why does this code print 0?
struct sockaddr_in my_addr = {2, 2}; // initialize sin_family and sin_port to 2, everything else value-initialized
my_addr = {0};
std::cout << my_addr.sin_port; // Why is this 0?
This is covered in the draft C++14 standard section 8.5.4 List-initialization which says:
List-initialization of an object or reference of type T is defined as follows:
and includes:
If T is an aggregate, aggregate initialization is performed (8.5.1).
and has the following example:
struct S2 {
int m1;
double m2, m3;
}
S2 s21 = { 1, 2, 3.0 }; // OK
S2 s22 { 1.0, 2, 3 }; // error: narrowing
S2 s23 { }; // OK: default to 0,0,0
and 8.5.1 Aggregates which says:
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)
Note that 8.5.4 is slightly different in C++11, it says:
List-initialization of an object or reference of type T is defined as
follows:
If the initializer list has no elements and T is a class
type with a default constructor, the object is value-initialized.
Otherwise, if T is an aggregate, aggregate initialization is performed
(8.5.1).
how come that this initializes every member to 0?
Because that's what C does when initializing structs, and C++ does the same for aggregate initialization for compatibility.
C does it because it's more convenient (it's usually what you want for members that you don't give an explicit value to) and safer that way (it doesn't leave dangerously uninitialized variables lying around).
If you really want the other members of the struct to remain uninitialized you can do it like this:
struct sockaddr_in s; // entirely uninitialized
s.sin_family = 0; // only initialize one member