Ambiguity between default-initialization and value-initialization - c++

I found many articles explaining the difference between "default-initialization and value-initialization" but in fact I didn't understand clearly.
Here's an example:
class A{
public:
int x;
};
int main(){
A a;// default initialization so x has undefined value.
A b = A(); // value initialization so x is a scalar thus it is value initialized to 0
}
Above it is OK as I guess but here:
int value = 4; // is this considered a value-initialization?
Please help me understand the major differences between the two forms of initializations.

A a; is default initialization, as the effect the default constructor of A is used for initialization. Since the implicitly-generated default constructor of A does nothing, a.x has indeterminate value.
A() is value initialization,
if T is a class type with a default constructor that is neither user-provided nor deleted (that is, it may be a class with an implicitly-defined or defaulted default constructor), the object is zero-initialized and then it is default-initialized if it has a non-trivial default constructor;
Note the difference with default initialization, A has an implicitly-defined default constructor, and the object is zero-initialized; so the data memeber x of the temporary object A() will be initialized to 0.
A b = A(); is copy initialization, in concept b is initialized from the temporary object A(), so b.x will be initialized to 0 too. Note that because of copy elision, since C++17 b is guaranteed to be value-initialized directly; the copy/move construction is omitted.
int value = 4; is copy initialization too. value will be initialized to 4.

Related

Different ways of initializing an object with default constructor in C++

struct A
{
int a;
std::string str;
};
A a;// 1
A a{};// 2
A a = {};// 3
A a = A();// 4
There seems to be all options. In case 1 and 4 a will be uninitialized, in 2 and 3 a will be initialized with zero and they both are same and just the matter of style or there is some difference? 4 supposed to first create a temporary object and then assign it to an a, but it will happen only if I will turn off comliler's optimization completely, right?
For all the cases the data member str is always default-initialized by the default constructor of std::string. Due to the different initialization styles, the data member a might be initialized to 0 or indeterminate value. In details,
The 1st one is default initialization, as the result a.a is initialized to indeterminate value (or gets zero-initialized to 0 if a is static or thread-local object), a.str is initialized by its default constructor.
The 2nd one is direct-list-initialization and aggregate initialization is performed, as the result a.a is value-initialized (zero-initialized) to 0, a.str is initialized by its default constructor.
The 3rd one is copy-list-initialization and aggregate initialization is performed, as the result a.a is value-initialized (zero-initialized) to 0, a.str is initialized by its default constructor.
In concept the 4th one is copy initialization, a is copy-initialized from A() (value-initialized temporary A). Because of copy elision (since C++17 it's mandatory) a might be value-initialized directly, as the result (which doesn't get changed by copy elision) a.a is zero-initialized to 0, a.str is initialized by its default constructor.

What Form Does the Implicitly-Declared Default Constructor Take?

So let's say I'm working with this toy example:
struct Foo {
int member;
};
I know that the default constructor won't default initialize member. So if I do this, member remains uninitialized: const Foo implicit_construction. As an aside this seems to work fine: const Foo value_initialization = Foo() though my understanding is that this isn't actually using the default constructor.
If I change Foo like this:
struct Foo {
Foo() = default;
int member;
};
And I try to do const Foo defaulted_construction, unsurprisingly it behaves exactly as implicit_construction did, with member being uninitialized.
Finally, if I change Foo to this:
struct Foo {
Foo(){};
int member;
};
And I do: const Foo defined_construction member is zero-initialized. I'm just trying to make sense of what the implicitly defined constructor looks like. I would have though it would have been Foo(){}. Is this not the case? Is there some other black magic at work that makes my defined constructor behave differently than the defaulted one?
Edit:
Perhaps I'm being mislead here. defaulted_construction is definitely uninitialized.
While defined_construction is definitely initialized.
I took this to be standardized behavior, is that incorrect?
What you're experiencing is called default initialization and the rules for it are (emphasis mine):
if T is a non-POD (until C++11) class type, the constructors are considered and subjected to overload resolution against the empty argument list. The constructor selected (which is one of the default constructors) is called to provide the initial value for the new object;
if T is an array type, every element of the array is default-initialized;
otherwise, nothing is done: the objects with automatic storage duration (and their subobjects) are initialized to indeterminate values.
Edit, in response to OP's request below:
Note that declaring a constructor = default does not change the situation (again, emphasis mine):
Implicitly-defined default constructor
If the implicitly-declared default constructor is not defined as deleted, it is defined (that is, a function body is generated and compiled) by the compiler if odr-used, and it has exactly the same effect as a user-defined constructor with empty body and empty initializer list. That is, it calls the default constructors of the bases and of the non-static members of this class.
Since the default constructor has an empty initializer list, its members satisfy the conditions for default initialization:
Default initialization is performed in three situations:
...
3) when a base class or a non-static data member is not mentioned in a constructor initializer list and that constructor is called.
Also note that you have to be careful when experimentally confirming this, because it's entirely possible that the default-initialized value of an int may be zero. In particular, you mentioned that this:
struct Foo {
Foo(){};
int member;
} foo;
Results in value-initialization, but it does not; here, member is default-initialized.
Edit 2:
Note the following distinction:
struct Foo {
int member;
};
Foo a; // is not value-initialized; value of `member` is undefined
Foo b = Foo(); // IS value-initialized; value of `member` is 0
This behavior can be understood by following the rules for value-initialization:
Value initialization is performed in these situations:
1,5) when a nameless temporary object is created with the initializer consisting of an empty pair of parentheses;
Form 1 (T();) is the form used on the right-hand side of the = above to initialize b.
The effects of value initialization are:
1) if T is a class type with no default constructor or with a user-provided or deleted default constructor, the object is default-initialized;
2) if T is a class type with a default constructor that is neither user-provided nor deleted (that is, it may be a class with an implicitly-defined or defaulted default constructor), the object is zero-initialized and then it is default-initialized if it has a non-trivial default constructor;
3) if T is an array type, each element of the array is value-initialized;
4) otherwise, the object is zero-initialized.
Finally though, note that in our earlier example:
struct Foo {
Foo(){}; // this satisfies condition (1) above
int member;
};
Foo f = Foo();
Now, condition (1) applies, and our (empty) user-declared constructor is called instead. Since this constructor does not initialize member, member is default-initialized (and its initial value is thus undefined).

Different performance for default initialized and value initialized struct

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.

Need some clarification with 8.5.p7 in the C++11 Standard

Paragraph 8.5p7 of the C++11 Standard states:
To value-initialize an object of type T means:
if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided 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 (possibly cv-qualified) non-union class type
without a user-provided constructor, then the object is
zero-initialized and, if T’s implicitly-declared default constructor
is non-trivial, that constructor is called.
if T is an array type,
then each element is value-initialized;
otherwise, the object is
zero-initialized.
I have a problem understanding the characters in bold above. How the additional calling of T's implicit default constructor could alter the zero-initialization, that has just occurred in this case?
Here's a concrete example:
class A {
int a;
public:
A() : a(1) {}
};
class B {
int b;
A c;
};
B falls in this category - it's a non-union class type without a user-provided constructor. So if a B is value-initialized, it will first be zero-initialized (so both b and c.a will be set to 0), and then the default constructor will be called (which will call A's constructor and set c.a to 1).
By the as-if rule, these might be combined into a single step by the optimizer (which will set b to 0 and c.a to 1), as no-one can ever see the object between the zero initialization and the default constructor.
T may not have its own explicit default constructor, but it may derive from U that does, and/or have a member of class type V that does.
struct S {
int a, b;
S() : b(10) {}
};
struct T {
S s;
};
int main() {
S s{};
T t{};
}
t is value initialized and T does not have a user-provided constructor. However T's implicitly declared default constructor is not trivial.
s is also value initialized but S has a user provided constructor.
s.a will have an indeterminate value. but t.s.a is zero due to the zero initialization that precedes the call of the default constructor. Both s.b and t.s.b are set to the value 10.

default values from not defined constructor

Bjarne wrote:-
For a type T, T() is the notation for the default value , as defined by the default constructor .
What happen when we don't declare default constructor ? For example
using namespace std;
class date{
int n,m;
public:
int day(){return n;}
int month(){return m;}
};//no default constructor
int main()
{
date any =date();
cout<<any.month()<<endl;
cout<<any.day()<<endl;
return 0;
}
Output of this program is 0 and 0 every time i run my program. I haven't declare any default constructor then why there exits a default value i.e. 0?
EDIT-
class date{
int n,m;
public:
date (){
m=1;}
int day(){return n;}
int month(){return m;}
};
int main()
{
date any =date();
cout<<any.month()<<endl;
cout<<any.day()<<endl;
return 0;
}
After reading answers i provide a default constructor but now n is getting garbage value but according to answers it should be 0 as m is out of reach of any other constructor and it is value initialisation as mentioned in answer
The behavior you see is Well-Defined for your class.
How & Why is the behavior Well-Defined?
The rule is:
If you do not provide a no argument constructor the compiler generates one for your program in case your program needs one.
Caveat:
The compiler does not generate the no argument constructor if your program defines any constructor for the class.
As per the C++ Standard an object can be initialized in 3 ways:
Zero Initialization
Default Initialization &
Value Initialization
When, a type name or constructor initializer is followed by () the initialization is through value initialization.
Thus,
date any =date();
^^^
Value Initializes an nameless object and then copies it in to the local object any,
while:
date any;
would be a Default Initialization.
Value Initialization gives an initial value of zero to members that are out of reach of any constructor.
In your program, n and m are beyond the reach of any constructor and hence get initialized to 0.
Answer to Edited Question:
In your edited case, your class provides a no argument constructor, date(), which is capable(& should) initialize members n and m, but this constructor doesn't initialize both the members, So In this case no zero initialization takes place, and the uninitialized members in the object have an Indeterminate(any random) value, further this temporary object is copied to any object which displays the shows indeterminate member values.
For Standerdese Fans:
The rules for object Initialization are aptly defined in:
C++03 Standard 8.5/5:
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 is zero-initialized;
— if T is an array type, each element is zero-initialized;
— if T is a reference type, no initialization is performed.
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.
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
Because compiler, cursing under its breath, generates one for you.
EDIT: Since Als said it does not answer question, I'll elaborate. When you use date any = date();, you call compiler-generated default constructor. This constructor calls default constructors for all base classes and data members. For your data members int default constructor is int(), which sets value to 0. Here is code on ideone.com
#include <iostream>
int main( void )
{
int i = -123;
i = int();
std::cout << i << std::endl;
return( 0 );
}
Program output:
0
From the C++ draft standard (December 1996 Working Paper):
If there is no user-declared constructor for class X, a default constructor is implicitly declared. An implicitly-declared default constructor is an inline public member of its class.