I originally wrote some code like this:
class Foo
{
public:
Foo() : m_buffer()
{}
private:
char m_buffer[1024];
};
Someone who is smarter than me said that having the m_buffer() initializer would zero out the memory. My intention was to leave the memory uninitialized. I didn't have time to discuss it further, but it piqued my curiosity.
Previously, I had thought it was wise to always list each member in the initializer list.
Could someone please describe this behavior further?
1) Why does the empty-paren initializer fill in memory?
2) Does it only hold for POD datatypes? I heard that it was so, but don't have the standard handy.
Thanks
If you have a member initialized like that, it will be value-initialized. That is also true for PODs. For a struct, every member is value-initialized that way, and for an array, every element of it is value-initialized.
Value-initialization for a scalar type like pointer or integer you will have it inialized to 0 converted to the right type. So you will get null pointers or false or whatever type you have concretely.
Note that the rule changed subtly from C++98 to C++03 (what we have right now), which can have surprising effects. C++98 didn't have that value-initialization. It said default initialization happens, which for a non-POD type always meant it's default constructor invokes. But value-initialization in C++03 has special meaning if there is no user-declared constructor: Every element is value-initialized then.
Here is the difference:
struct A { int c; ~A() { } }; // non-POD, but no user declared ctor
struct B { A a; B():a(){ } } b;
Now, in C++03, you will be guaranteed that b.a.c is zero. While in C++98, b.a.c will have some indeterminated value.
Previously, I had thought it was wise
to always list each member in the
initializer list.
This is to make sure all the members are initialized.
To solve your task simply remove m_buffer from the initializer list.
template <typename T>
struct C
{
C():
buff(),
var(),
object()
{
}
T buff[128];
T var;
std::string object;
};
Whatever T type is using T() is go for default constructor. For int, chars, etc it is 0, for arrays it is {T()}. And for classes it is simply their default constructor.
Related
The C++ standard (section 8.5) says:
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.
Why? I can't think of any reason why a user-provided constructor is required in this case.
struct B{
B():x(42){}
int doSomeStuff() const{return x;}
int x;
};
struct A{
A(){}//other than "because the standard says so", why is this line required?
B b;//not required for this example, just to illustrate
//how this situation isn't totally useless
};
int main(){
const A a;
}
The reason is that if the class doesn't have a user-defined constructor, then it can be POD, and the POD class is not initialized by default. So if you declare a const object of POD which is uninitialized, what use of it? So I think the Standard enforces this rule so that the object can actually be useful.
struct POD
{
int i;
};
POD p1; //uninitialized - but don't worry we can assign some value later on!
p1.i = 10; //assign some value later on!
POD p2 = POD(); //initialized
const POD p3 = POD(); //initialized
const POD p4; //uninitialized - error - as we cannot change it later on!
But if you make the class a non-POD:
struct nonPOD_A
{
nonPOD_A() {} //this makes non-POD
};
nonPOD_A a1; //initialized
const nonPOD_A a2; //initialized
Note the difference between POD and non-POD.
User-defined constructor is one way to make the class non-POD. There are several ways you can do that.
struct nonPOD_B
{
virtual void f() {} //virtual function make it non-POD
};
nonPOD_B b1; //initialized
const nonPOD_B b2; //initialized
Notice nonPOD_B doesn't defined user-defined constructor. Compile it. It will compile:
http://www.ideone.com/h7TsA
And comment the virtual function, then it gives error, as expected:
http://www.ideone.com/SWk7B
Well, I think, you misunderstood the passage. It first says this (§8.5/9):
If no initializer is specified for an object, and the object is of (possibly cv-qualified) non-POD class type (or array thereof), the object shall be default-initialized; [...]
It talks about non-POD class possibly cv-qualified type. That is, the non-POD object shall be default-initialized if there is no initializer specified. And what is default-initialized? For non-POD, the spec says (§8.5/5),
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);
It simply talks about default constructor of T, whether its user-defined or compiler-generated is irrelevant.
If you're clear up to this, then understand what the spec next says ((§8.5/9),
[...]; if the object is of const-qualified type, the underlying class type shall have a user-declared default constructor.
So this text implies, the program will be ill-formed if the object is of const-qualified POD type, and there is no initializer specified (because POD are not default initialized):
POD p1; //uninitialized - can be useful - hence allowed
const POD p2; //uninitialized - never useful - hence not allowed - error
By the way, this compiles fine, because its non-POD, and can be default-initialized.
Pure speculation on my part, but consider that other types have a similar restriction, too:
int main()
{
const int i; // invalid
}
So not only is this rule consistent, but it also (recursively) prevents unitialized const (sub)objects:
struct X {
int j;
};
struct A {
int i;
X x;
}
int main()
{
const A a; // a.i and a.x.j in unitialized states!
}
As for the other side of the question (allowing it for types with a default constructor), I think the idea is that a type with a user-provided default constructor is supposed to always be in some sensible state after construction. Note that the rules as they are allow for the following:
struct A {
explicit
A(int i): initialized(true), i(i) {} // valued constructor
A(): initialized(false) {}
bool initialized;
int i;
};
const A a; // class invariant set up for the object
// yet we didn't pay the cost of initializing a.i
Then perhaps we could formulate a rule like 'at least one member must be sensibly initialized in a user-provided default constructor', but that's way too much time spent trying to protect against Murphy. C++ tends to trust the programmer on certain points.
This was considered a defect (against all versions of the standard) and it was resolved by Core Working Group (CWG) Defect 253. The new wording for the standard states in http://eel.is/c++draft/dcl.init#7
A class type T is const-default-constructible if
default-initialization of T would invoke a user-provided constructor
of T (not inherited from a base class) or if
each direct non-variant non-static data member M of T has a default member initializer or, if M is of class type X (or array thereof), X
is const-default-constructible,
if T is a union with at least one non-static data member, exactly one variant member has a default member initializer,
if T is not a union, for each anonymous union member with at least one non-static data member (if any), exactly one non-static data
member has a default member initializer, and
each potentially constructed base class of T is const-default-constructible.
If a program calls for the default-initialization of an object of a
const-qualified type T, T shall be a const-default-constructible class
type or array thereof.
This wording essentially means that the obvious code works. If you initialize all of your bases and members, you can say A const a; regardless of how or if you spell any constructors.
struct A {
};
A const a;
gcc has accepted this since 4.6.4. clang has accepted this since 3.9.0. Visual Studio also accepts this (at least in 2017, not sure if sooner).
I was watching Timur Doumler's talk at Meeting C++ 2018 and I finally realised why the standard requires a user-provided constructor here, not merely a user-declared one. It has to do with the rules for value initialisation.
Consider two classes: A has a user-declared constructor, B has a user-provided constructor:
struct A {
int x;
A() = default;
};
struct B {
int x;
B() {}
};
At first glance, you might think these two constructors will behave the same. But see how value initialisation behaves differently, while only default initialisation behaves the same:
A a; is default initialisation: the member int x is uninitialised.
B b; is default initialisation: the member int x is uninitialised.
A a{}; is value initialisation: the member int x is zero-initialised.
B b{}; is value initialisation: the member int x is uninitialised.
Now see what happens when we add const:
const A a; is default initialisation: this is ill-formed due to the rule quoted in the question.
const B b; is default initialisation: the member int x is uninitialised.
const A a{}; is value initialisation: the member int x is zero-initialised.
const B b{}; is value initialisation: the member int x is uninitialised.
An uninitialised const scalar (e.g. the int x member) would be useless: writing to it is ill-formed (because it's const) and reading from it is UB (because it holds an indeterminate value). So this rule prevents you from creating such a thing, by forcing you to either add an initialiser or opt-in to the dangerous behaviour by adding a user-provided constructor.
I think it would be nice to have an attribute like [[uninitialized]] to tell the compiler when you're intentionally not initialising an object. Then we wouldn't be forced to make our class not trivially default constructible to get around this corner case. This attribute has actually been proposed, but just like all the other standard attributes, it does not mandate any normative behaviour, being merely a hint to the compiler.
Congratulations, you've invented a case in which there need not be any user defined constructor for the const declaration with no initializer to make sense.
Now can you come up with a reasonable re-wording of the rule that covers your case but still makes the cases that should be illegal illegal? Is it less than 5 or 6 paragraphs? Is it easy and obvious how it should be applied in any situation?
I posit that coming up with a rule that allows the declaration you created to make sense is really hard, and making sure that the rule can be applied in a way that makes sense to people when reading code is even harder. I would prefer a somewhat restrictive rule that was the right thing to do in most cases to a very nuanced and complex rule that was difficult to understand and apply.
The question is, is there a compelling reason the rule should be more complex? Is there some code that would otherwise be very difficult to write or understand that can be written much more simply if the rule is more complex?
I have a simple struct with an array:
struct A
{
uint32_t arr[size];
};
I have two functions, which create it using default initialization and value initialization:
template<class T>
void testDefault()
{
T* pa = new T; // Default
use(*pa);
delete pa;
}
template<class T>
void testValue()
{
T* pa = new T(); // Value
use(*pa);
delete pa;
}
I'm facing different performance for those functions. The funny thing is that performance differences vary depending on how I declare default constructor of the struct. I have three ways:
struct A
{
uint32_t arr[size];
// Implicit constructor
};
struct B
{
uint32_t arr[size];
B() {}; // Empty constructor
};
struct C
{
uint32_t arr[size];
C() = default; // Defaulted constructor
};
I thought they are all the same from compiler's point of view. Never have been I so wrong. I did run both testDefault() and testValue() several times with structs A, B and C and measured performance. Here is what I have:
Default initialization (implict constructor) done in 880ms
Value initialization (implict constructor) done in 1145ms
Default initialization (empty constructor) done in 867ms
Value initialization (empty constructor) done in 865ms
Default initialization (defaulted constructor) done in 872ms
Value initialization (defaulted constructor) done in 1148ms
Note how performance is clearly worse for both implicit and defaulted constructors. Only empty constructor correctly shows the same performance for both different initialization forms.
I tested this with VC++, gcc and clang. See online demo for gcc. Timings are quite persistent.
What I assume is:
Default and value initializations for UDT are the same thing
All demonstrated ways of defining default constructor are doing the same thing
Default constructor of these structs should leave content of the array in indeterminate state
Since all the compilers exhibit the same timings, it seems like I'm missing something. Can anyone please explain me these timings?
(See also my question Why compilers put zeros into arrays while they do not have to? on the same topic. I give some links to cppreference there.)
Let's look at the definition of value-initialize:
To value-initialize an object of type T means:
if T is a (possibly cv-qualified) class type with either no default constructor (12.1) or a default constructor that is user-provided or deleted, then the object is default-initialized;
if T is a (possibly cv-qualified) class type without a user-provided or deleted default constructor, then the object is zero-initialized [...];
if T is an array type, then each element is value-initialized;
otherwise, the object is zero-initialized.
Also let's review the adjectives in use here for constructors:
user-declared - you declared the constructor
user-provided - you declared the constructor and didn't set it to = default or = delete
default - can be called with no arguments
declared as defaulted - marked = default; or implicitly generated as such
Looking at your classes:
A has a default constructor which is implicitly declared as defaulted, and not user-provided.
C has a default constructor which is user-declared as defaulted, and not user-provided.
So the second bullet point in the definition of value-initialize applies. The object is zero-initialized, meaning the arr is zeroed out.
B has a default constructor which is user-provided.
So the first bullet point of value-initialize applies to B; value-initialization is the same as default-initialization here, and arr is not zeroed out.
Your timings correctly seem to correspond to what is expected: value-initialization of A or C zeroes out arr and the other cases don't.
One of the constructors behaves differently under value initialization. In this version,
B() {};
the array B::arr is not value-initialized when the B is. With the others, it is. Whether this explains the performance difference is another matter.
So, B::arr doesn't get zero-initialized with value initialization, whereas A::arr and C::arr do. All three cases have the same behaviour under default initialization, that is to say, arr gets default-initialized, i.e. no initialization is performed.
I frequently come across POD structs in code that are manually zero-initialized with memset like so:
struct foo;
memset(&foo, 0, sizeof(foo));
I checked the C++11 standard and it says: "An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized." Followed by: "To value-initialize a [pod struct] of type T means ... the object is zero-initialized."
So... does this mean you can always safely condense the above code to just the following:
struct foo{};
And have a guaranteed initialized struct as if you had called memset(&foo, 0, ...)?
If so, then generally speaking, can you safely initialize anything with empty initializers like so:
SomeUnknownType foo{}; // will 'foo' be completely "set" to known values?
I know this wasn't always possible in C++03 (before uniform initialization syntax) but is it possible now in C++11 for any type?
You can certainly initialize any standard layout type using empty parenthesis and get it zero initialized. Once you add constructors into the picture, this isn't necessarily true, unfortunately:
struct foo {
int f;
};
struct bar {
int b;
bar() {}
};
foo f{};
bar b{};
While f.f is zero initialized, b.b is uninitialized. However, there isn't anything you can do about bar because you can't memset() it either. It needs to be fixed.
Memset will initialize it to 0 but empty parenthesis will value initialze your object(there is a difference).
Quoting from the standard:
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);
Also, doing struct foo{}; is safe and it takes care of not reseting the virtual table pointer.
But doing memset(&foo, 0, sizeof(foo)); in case of virtual functions is really really bad as it resets the pointer to the virtual table.
The C++ standard (section 8.5) says:
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.
Why? I can't think of any reason why a user-provided constructor is required in this case.
struct B{
B():x(42){}
int doSomeStuff() const{return x;}
int x;
};
struct A{
A(){}//other than "because the standard says so", why is this line required?
B b;//not required for this example, just to illustrate
//how this situation isn't totally useless
};
int main(){
const A a;
}
The reason is that if the class doesn't have a user-defined constructor, then it can be POD, and the POD class is not initialized by default. So if you declare a const object of POD which is uninitialized, what use of it? So I think the Standard enforces this rule so that the object can actually be useful.
struct POD
{
int i;
};
POD p1; //uninitialized - but don't worry we can assign some value later on!
p1.i = 10; //assign some value later on!
POD p2 = POD(); //initialized
const POD p3 = POD(); //initialized
const POD p4; //uninitialized - error - as we cannot change it later on!
But if you make the class a non-POD:
struct nonPOD_A
{
nonPOD_A() {} //this makes non-POD
};
nonPOD_A a1; //initialized
const nonPOD_A a2; //initialized
Note the difference between POD and non-POD.
User-defined constructor is one way to make the class non-POD. There are several ways you can do that.
struct nonPOD_B
{
virtual void f() {} //virtual function make it non-POD
};
nonPOD_B b1; //initialized
const nonPOD_B b2; //initialized
Notice nonPOD_B doesn't defined user-defined constructor. Compile it. It will compile:
http://www.ideone.com/h7TsA
And comment the virtual function, then it gives error, as expected:
http://www.ideone.com/SWk7B
Well, I think, you misunderstood the passage. It first says this (§8.5/9):
If no initializer is specified for an object, and the object is of (possibly cv-qualified) non-POD class type (or array thereof), the object shall be default-initialized; [...]
It talks about non-POD class possibly cv-qualified type. That is, the non-POD object shall be default-initialized if there is no initializer specified. And what is default-initialized? For non-POD, the spec says (§8.5/5),
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);
It simply talks about default constructor of T, whether its user-defined or compiler-generated is irrelevant.
If you're clear up to this, then understand what the spec next says ((§8.5/9),
[...]; if the object is of const-qualified type, the underlying class type shall have a user-declared default constructor.
So this text implies, the program will be ill-formed if the object is of const-qualified POD type, and there is no initializer specified (because POD are not default initialized):
POD p1; //uninitialized - can be useful - hence allowed
const POD p2; //uninitialized - never useful - hence not allowed - error
By the way, this compiles fine, because its non-POD, and can be default-initialized.
Pure speculation on my part, but consider that other types have a similar restriction, too:
int main()
{
const int i; // invalid
}
So not only is this rule consistent, but it also (recursively) prevents unitialized const (sub)objects:
struct X {
int j;
};
struct A {
int i;
X x;
}
int main()
{
const A a; // a.i and a.x.j in unitialized states!
}
As for the other side of the question (allowing it for types with a default constructor), I think the idea is that a type with a user-provided default constructor is supposed to always be in some sensible state after construction. Note that the rules as they are allow for the following:
struct A {
explicit
A(int i): initialized(true), i(i) {} // valued constructor
A(): initialized(false) {}
bool initialized;
int i;
};
const A a; // class invariant set up for the object
// yet we didn't pay the cost of initializing a.i
Then perhaps we could formulate a rule like 'at least one member must be sensibly initialized in a user-provided default constructor', but that's way too much time spent trying to protect against Murphy. C++ tends to trust the programmer on certain points.
This was considered a defect (against all versions of the standard) and it was resolved by Core Working Group (CWG) Defect 253. The new wording for the standard states in http://eel.is/c++draft/dcl.init#7
A class type T is const-default-constructible if
default-initialization of T would invoke a user-provided constructor
of T (not inherited from a base class) or if
each direct non-variant non-static data member M of T has a default member initializer or, if M is of class type X (or array thereof), X
is const-default-constructible,
if T is a union with at least one non-static data member, exactly one variant member has a default member initializer,
if T is not a union, for each anonymous union member with at least one non-static data member (if any), exactly one non-static data
member has a default member initializer, and
each potentially constructed base class of T is const-default-constructible.
If a program calls for the default-initialization of an object of a
const-qualified type T, T shall be a const-default-constructible class
type or array thereof.
This wording essentially means that the obvious code works. If you initialize all of your bases and members, you can say A const a; regardless of how or if you spell any constructors.
struct A {
};
A const a;
gcc has accepted this since 4.6.4. clang has accepted this since 3.9.0. Visual Studio also accepts this (at least in 2017, not sure if sooner).
I was watching Timur Doumler's talk at Meeting C++ 2018 and I finally realised why the standard requires a user-provided constructor here, not merely a user-declared one. It has to do with the rules for value initialisation.
Consider two classes: A has a user-declared constructor, B has a user-provided constructor:
struct A {
int x;
A() = default;
};
struct B {
int x;
B() {}
};
At first glance, you might think these two constructors will behave the same. But see how value initialisation behaves differently, while only default initialisation behaves the same:
A a; is default initialisation: the member int x is uninitialised.
B b; is default initialisation: the member int x is uninitialised.
A a{}; is value initialisation: the member int x is zero-initialised.
B b{}; is value initialisation: the member int x is uninitialised.
Now see what happens when we add const:
const A a; is default initialisation: this is ill-formed due to the rule quoted in the question.
const B b; is default initialisation: the member int x is uninitialised.
const A a{}; is value initialisation: the member int x is zero-initialised.
const B b{}; is value initialisation: the member int x is uninitialised.
An uninitialised const scalar (e.g. the int x member) would be useless: writing to it is ill-formed (because it's const) and reading from it is UB (because it holds an indeterminate value). So this rule prevents you from creating such a thing, by forcing you to either add an initialiser or opt-in to the dangerous behaviour by adding a user-provided constructor.
I think it would be nice to have an attribute like [[uninitialized]] to tell the compiler when you're intentionally not initialising an object. Then we wouldn't be forced to make our class not trivially default constructible to get around this corner case. This attribute has actually been proposed, but just like all the other standard attributes, it does not mandate any normative behaviour, being merely a hint to the compiler.
Congratulations, you've invented a case in which there need not be any user defined constructor for the const declaration with no initializer to make sense.
Now can you come up with a reasonable re-wording of the rule that covers your case but still makes the cases that should be illegal illegal? Is it less than 5 or 6 paragraphs? Is it easy and obvious how it should be applied in any situation?
I posit that coming up with a rule that allows the declaration you created to make sense is really hard, and making sure that the rule can be applied in a way that makes sense to people when reading code is even harder. I would prefer a somewhat restrictive rule that was the right thing to do in most cases to a very nuanced and complex rule that was difficult to understand and apply.
The question is, is there a compelling reason the rule should be more complex? Is there some code that would otherwise be very difficult to write or understand that can be written much more simply if the rule is more complex?
Let's say I have the following structure declaration (simple struct with no constructor).
struct Foo
{
int x;
int y;
int z;
char szData[DATA_SIZE];
};
Now let's say this struct is a member of a C++ class as follows:
class CFoobar
{
Foo _foo;
public:
CFoobar();
};
If I declare CFoobar's constructor as follows:
CFoobar::CFoobar()
{
printf("_foo = {%d, %d, %d}\n", _foo.x, _foo.y,_foo.z);
for (int x = 0; x < 100; x++)
printf("%d\n", _foo.szData[x]);
}
As you would expect, when CFoobar's constructor runs, garbage data gets printed out Obviously, the easy fix is to memset or ZeroMemory &_foo. It's what I've always done...
However, I did notice that if add _foo to the constructor's initialization list with no parameters as follows:
CFoobar::CFoobar()
: _foo()
{
That this appears to zero-out the member variables of _foo. At least that was the case with g++ on linux.
Now here's my question: Is this standard C++, or is this compiler specific behavior?
If it's standard behavior, can someone quote me a reference from an official source? Any "gotchas" in regards to implicit zero-init behavior with more complicated structs and classes?
Yes, this is defined behaviour according to the standard. 12.6.2 [class.base.init] / 3 : "if the expression-list of the mem-initializer is omitted, the base class or member subobject is value-initialized."
Be warned, though, if Foo wasn't a POD-type but still had no user-declared constructor (e.g. it had a std::string type) then some very popular compilers would not correctly value-initialize it.
All compilers that I know of do correctly perform value-initialization of POD members when you use () as the initializer in a constructor initializer-list.
i find it hard to read the standard, but I found it I think:
To value-initialize an object of type T means:
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
Value-initialization for such a class object may be implemented by zero-initializing the object and then calling the default constructor.
Section 8.5
It's the equivalent of float foo = float();
It will zero the object, even if the value representation is not all-bits-zero. I.e. it's even better than memset().