C++11 strange brace initialization behavior - c++

I don't understand how C++11 brace initialization rules work here.
Having this code:
struct Position_pod {
int x,y,z;
};
class Position {
public:
Position(int x=0, int y=0, int z=0):x(x),y(y),z(z){}
int x,y,z;
};
struct text_descriptor {
int id;
Position_pod pos;
const int &constNum;
};
struct text_descriptor td[3] = {
{0, {465,223}, 123},
{1, {465,262}, 123},
};
int main()
{
return 0;
}
Note, that the array is declared to have 3 elements, but only 2 initializers are provided.
However it compiles without error, which sounds strange, as the last array element's reference member will be uninitialized. Indeed, it has NULL value:
(gdb) p td[2].constNum
$2 = (const int &) #0x0: <error reading variable>
And now the "magic": I changed Position_pod to Position
struct text_descriptor {
int id;
Position_pod pos;
const int &constNum;
};
becomes this:
struct text_descriptor {
int id;
Position pos;
const int &constNum;
};
and now it gives the expected error:
error: uninitialized const member ‘text_descriptor::constNum'
My question: Why it compiles in the first case, when it should give an error (as in the second case).
The difference is, that Position_pod uses C - style brace initialization and Position uses C++11 - style initialization, which call Position's constructor. But how does this affect the possibility to leave a reference member uninitialized?
(Update)
Compiler:
gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2

It will be clear that
struct text_descriptor td[3] = {
{0, {465,223}, 123},
{1, {465,262}, 123},
};
is list-initialisation, and that the initialiser list is not empty.
C++11 says ([dcl.init.list]p3):
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).
...
[dcl.init.aggr]p1:
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), no base classes (Clause 10), and no virtual functions (10.3).
td is an array, so it is an aggregate, so aggregate initialisation is performed.
[dcl.init.aggr]p7:
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 (8.5.4).
This is the case here, so td[2] is initialised from an empty initialiser list, which ([dcl.init.list]p3 again) means it is value-initialised.
Value-initialisation, in turn, means ([dcl.init]p7):
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), ...
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.
...
Your class text_descriptor is a class with no user-provided constructor, so td[2] is first zero-initialised, and then its constructor is called.
Zero-initialisation means ([dcl.init]p5):
To zero-initialize an object or reference of type T means:
if T is a scalar type (3.9), ...
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;
if T is a (possibly cv-qualified) union type, ...
if T is an array type, ...
if T is a reference type, no initialization is performed.
This is well-defined regardless of text_descriptor's default constructor: it just zero-initialises the non-reference members and sub-members.
Then the default constructor is called, if it is non-trivial. Here's how the default constructor is defined ([special]p5):
A default constructor for a class X is a constructor of class X that can be called without an argument. If there is no user-declared constructor for class X, a constructor having no parameters is implicitly declared
as defaulted (8.4). An implicitly-declared default constructor is an inline public member of its class. A defaulted default constructor for class X is defined as deleted if:
...
any non-static data member with no brace-or-equal-initializer is of reference type,
...
A default constructor is trivial if it is not user-provided and if:
its class has no virtual functions (10.3) and no virtual base classes (10.1), and
no non-static data member of its class has a brace-or-equal-initializer, and
all the direct base classes of its class have trivial default constructors, and
for all the non-static data members of its class that are of class type (or array thereof), each such class has a trivial default constructor.
Otherwise, the default constructor is non-trivial.
So, the implicitly defined constructor is deleted, as expected, but it is also trivial, if pos is a POD type (!). Because the constructor is trivial, it is not called. Because the constructor is not called, the fact that it is deleted is not a problem.
This is a gaping hole in C++11, which has since been fixed. It happened to have been fixed to deal with inaccessible trivial default constructors, but the fixed wording also covers deleted trivial default constructors. N4140 (roughly C++14) says in [dcl.init.aggr]p7 (emphasis mine):
if T is a (possibly cv-qualified) class type without a user-provided or deleted default constructor, then the object is zero-initialized and the semantic constraints for default-initialization are checked, and if
T has a non-trivial default constructor, the object is default-initialized;
As T.C. pointed out in the comments, another DR also changed so that td[2] is still initialised from an empty initialiser list, but that empty initialiser list now implies aggregate initialisation. That, in turn, implies each of td[2]'s members is initialised from an empty initialiser list as well ([dcl.init.aggr]p7 again), so would seem to initialise the reference member from {}.
[dcl.init.aggr]p9 then says (as remyabel had pointed out in a now-deleted answer):
If an incomplete or empty initializer-list leaves a member of reference type uninitialized, the program is ill-formed.
It is unclear to me that this applies to references initialised from the implicit {}, but compilers do interpret it as such, and there's not much else that could be meant by it.

The first version (the one with _pod suffix) still works but gives no error because for the z value, the default value of int is chosen (the 0). Idem for the const int reference.
In the second version, you cannot define a const reference without giving it a value. The compiler gives you an error because later you cannot assign any value to it.
In addition, the compiler you're using plays an important role here, maybe it's a bug, just because you are declaring a class member before an int member.

Related

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).

Const member stack vs heap

If I try to compile this code
struct A {
const int j;
};
A a;
I'll get an expected error:
error: uninitialized const member in ‘struct A’
but, if I try to compile this one:
struct A {
const int j;
};
A * a = new A();
I'll get a successful build.
The question is: why does new allocation allows creating a variable with const member without explicit constructor and stack allocation - doesn't ?
It's not because of the heap allocation, but because of the parenthesis you use when allocating. If you do e.g.
A* a = new A;
it would also fail.
The reason it works when you add the parenthesis is because then your structure is value initialized, and for a POD-type like A value-initialization value-initializes each member, and the default value-initialization for int is zero.
That means it would probably work creating the variable on the stack as well, if you just add the value-initialization parenthesis:
A a = A(); // watch out for the http://en.wikipedia.org/wiki/Most_vexing_parse
Though that brings other potential problems, better use uniform initialization if you can (requies C++11):
A a{};
This is ill-formed as of C++14. §12.1 [class.ctor] says that
4 A defaulted default constructor for class X is defined as deleted
if:
[...]
any non-variant non-static data member of const-qualified type (or array thereof) with no brace-or-equal-initializer does not have a
user-provided default constructor,
[...]
A default constructor is trivial if it is not user-provided and if:
its class has no virtual functions (10.3) and no virtual base classes (10.1), and
no non-static data member of its class has a brace-or-equal-initializer, and
all the direct base classes of its class have trivial default constructors, and
for all the non-static data members of its class that are of class type (or array thereof), each such class has a trivial default
constructor.
§8.5 [dcl.init] says in turn that
7 To default-initialize an object of type T means:
if T is a (possibly cv-qualified) class type (Clause 9), the default constructor (12.1) for T is called (and the initialization is
ill-formed if T has no default constructor or overload resolution
(13.3) results in an ambiguity or in a function that is deleted or
inaccessible from the context of the initialization);
[...]
8 To value-initialize an object of type T means:
if T is a (possibly cv-qualified) class type (Clause 9) with either no default constructor (12.1) or a default constructor that is
user-provided or deleted, then the object is default-initialized;
[...]
The empty pair of parentheses results in value-initialization. The default constructor for A is defined as deleted per [class.ctor]/p4. Thus, by [dcl.init]/p8, value-initialization means default-initialization, and by p7 the initialization is ill-formed because the constructor is deleted.
The C++11 version actually allowed value-initialization in this context; it says for value-initialization that, among other things,
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.
Since by the definition above the default constructor is trivial (even though it is deleted), it is not actually called. This was considered a defect in the standard and the relevant wording was changed by CWG issue 1301.
Compiler vendors usually do implement defect resolutions, so this could be considered a bug in GCC 4.8 that's been fixed in 4.9.

Value initialization: default initialization or zero initialization?

I have templated gray_code class which is meant to store some unsigned integer whose underlying bits are stored in Gray code order. Here it is:
template<typename UnsignedInt>
struct gray_code
{
static_assert(std::is_unsigned<UnsignedInt>::value,
"gray code only supports built-in unsigned integers");
// Variable containing the gray code
UnsignedInt value;
// Default constructor
constexpr gray_code()
= default;
// Construction from UnsignedInt
constexpr explicit gray_code(UnsignedInt value):
value( (value >> 1) ^ value )
{}
// Other methods...
};
In some generic algorithm, I wrote something like this:
template<typename UnsignedInt>
void foo( /* ... */ )
{
gray_code<UnsignedInt> bar{};
// Other stuff...
}
In this piece of code, I expected bar to be zero-intialized and therefore bar.value to be zero-initialized. However, after having struggled with unexpected bugs, it appears that bar.value is initialized with garbage (4606858 to be exact) instead of 0u. That surprised me, so I went to cppreference.com to see what the line above was exactly supposed to do...
From what I can read, the form T object{}; corresponds to value initialization. I found this quote interesting:
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.
However, gray_code has a user-provided constructor. Therefore it is not an aggregate thus aggregate initialization is not performed. gray_code has no constructor taking an std::initializer_list so list initialization is not performed either. The value-initialized of gray_code should then follow the usual C++14 rules of value initialization:
1) If T is a class type with no default constructor or with a user-provided default constructor or with a deleted default constructor, the object is default-initialized.
2) If T is a class type without a user-provided or deleted default constructor (that is, it may be a class with a defaulted default constructor or with an implicitly-defined one) then 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.
If I read correctly, gray_code has an explicitly defaulted (not user-provided) default constructor, therefore 1) does not apply. It has a defaulted default constructor, so 2) applies: gray_code is zero-initialized. The defaulted default constructor seems to meet all the requirements of a trivial default constructor, so default initialization should not happen. Let's have a look then at how gray_code is zero-initialized:
If T is a scalar type, the object's initial value is the integral constant zero implicitly converted to T.
If T is an non-union class type, all base classes and non-static data members are zero-initialized, and all padding is initialized to zero bits. The constructors, if any, are ignored.
If T is a union type, the first non-static named data member is zero-initialized and all padding is initialized to zero bits.
If T is array type, each element is zero-initialized
If T is reference type, nothing is done.
gray_code is a non-union class type. Therefore, all of its non-static data members should be initialized which means that value is zero-initialized. value satisfies std::is_unsigned and is therefore a scalar type, which means that it should be initialized with "the integral constant zero implicitly converted to T".
So, if I read correctly all of that, in the function foo above, bar.value should always be initialized with 0 and it should never be initialized with garbage, am I right?
Note: the compiler I compiled my code with is MinGW_w4 GCC 4.9.1 with (POSIX threads and dwarf exceptions) in case that helps. While I sometimes get garbage on my computer, I never managed to get anything else than zero with online compilers.
Update: It seems to be a GCC bug that the error is mine and not that of my compiler. Actually, when writing this question, I assumed for the sake of simplicity that
class foo {
foo() = default;
};
and
class foo {
foo();
};
foo::foo() = default;
were equivalent. They are not. Here is the quote from the C++14 standard, section [dcl.fct.def.default]:
A function is user-provided if it is user-declared and not explicitly defaulted or
deleted on its first declaration.
In other words, when I got garbage values, my defaulted default constructor was indeed user-provided since it was not explicitly efaulted on its first declaration. Therefore, what happened was not zero initialization but default initialization. Thanks #Columbo again for pointing out the real problem.
So, if I read correctly all of that, in the function foo above,
bar.value should always be initialized with 0 and it should never be
initialized with garbage, am I right?
Yes. Your object is direct-list-initialized. C++14's* [dcl.init.list]/3 specifies that
List-initialization of an object or reference of type T is defined
as follows:
[… Inapplicable bullet points…]
Otherwise, if T is an aggregate, aggregate initialization is performed (8.5.1).
Otherwise, if the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.
[…]
Your class isn't an aggregate since it has user-provided constructors, but it does have a default constructor. [dcl.init]/7:
To value-initialize an object of type T means:
if T is a (possibly cv-qualified) class type (Clause 9) 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 and the semantic constraints for
default-initialization are checked, and if T has a non-trivial
default constructor, the object is default-initialized;
[dcl.fct.def.default]/4:
A special member function is user-provided if it is user-declared and
not explicitly defaulted […] on its first declaration.
So your constructor is not user-provided, therefore the object is zero-initialized. (The constructor is not called since its trivial)
And finally, in case this was not clear, to zero-initialize an object or reference of type T means:
if T is a scalar type (3.9), the object is initialized to the value obtained by converting the integer literal 0 (zero) to T;
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;
[…]
Thus either
Your compiler is bugged
…or your code triggers undefined behavior at some other point.
* The answer is still yes in C++11, though the quoted sections are not equivalent.

Confused on how empty user defined constructor will initialize non-static non-POD member variables

I know that default initialization for non-POD types will also default initialize non-static non-POD member variables by calling their default constructor. But I'm not sure exactly how this happens. Here is an example of what I mean:
#include <iostream>
#include <vector>
using namespace std;
class Test2 {
public:
Test2() {cout <<"Here";}
};
class Test {
public:
Test() {}
Test2 i;
};
int main() {
Test foo;
}
The output is:
Here
Based on the C++ standard on initializers (8.5), for default initialization:
— 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);
So given this, I do expect that the default constructor Test() will get called, but my empty default constructor for the class Test does not initialize Test2 i explicitly yet clearly, Test2() is getting called implicitly somehow. What I'm wondering is how this happens?
Similarly, for value initialization (not related to example above), if an empty user defined default constructor does not explicitly zero initialize a POD non-static member variable, how does that variable get zero initialized (which I know it does do)? Since based on the standard, it seems that for value initialization, all that happens when you have a user defined default constructor is that the constructor gets called.
The corresponding part of the C++ standard for value initialization is the following:
— 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);
This question is similar to c++ empty constructor and member initialization
But the difference is that instead of asking what the end result behavior is, I'd like to know why the end result behavior happens.
In the C++11 standard, section 12.6 paragraph 8:
In a non-delegating constructor, if a given non-static data member or base class is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then
if the entity is a non-static data member that has a brace-or-equal-initializer, the entity is initialized
as specified in 8.5;
otherwise, if the entity is a variant member (9.5), no initialization is performed;
otherwise, the entity is default-initialized (8.5).
You are encountering the third case, where there is no initializer for the member and the member isn't a variant member, so in that case it is default-initialized.
Also, from paragraph 10:
In a non-delegating constructor, initialization proceeds in the following order:
- First, and only for the constructor of the most derived class (1.8), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.
Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list
(regardless of the order of the mem-initializers).
Then, non-static data members are initialized in the order they were declared in the class definition
(again regardless of the order of the mem-initializers).
Finally, the compound-statement of the constructor body is executed.
Regardless of what you specify in your constructor, the members are going to be initialized just before the body of the constructor is executed.
A mem-initializer-id is the identifier used to refer to a member in a constructor initializer list:
class Test {
public:
Test() : i() {} // Here `i` is a mem-initializer-id
Test2 i;
};
Value initialization of a type with a user-defined default constructor performs no initialization on non-static POD members if they are not explicitly initialized in the constructor. E.g., in this program:
#include <iostream>
using namespace std;
struct Foo {
// Foo has a user-defined default constructor
// that does not initialize i
Foo() {}
int i;
};
int main() {
Foo x{}; // value-initialize x
cout << x.i << endl;
}
x.i is uninitialized. The program therefore technically has undefined behavior, but in this case "undefined behavior" most likely means that it will print an unspecified integer value that is likely not 0.
Language lawyer argument:
§12.6.1p2: "An object of class type can also be initialized by a braced-init-list. List-initialization semantics apply; see 8.5 and 8.5.4."
§8.5.4p3: "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."
§8.5p7: "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)
According to the draft of the C++ standard found at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1905.pdf, section 12.6.2:
If a given non-static data member or base class is not named by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer), then
— If the entity is a non-static data member of (possibly cv-qualified) class type (or array thereof) or a base class, and the entity class is a non-POD class, the entity is default-initialized (8.5). If the entity is a non-static data member of a const-qualified type, the entity class shall have a user-declared default constructor.
— Otherwise, the entity is not initialized. If the entity is of const-qualified type or reference type, or of a (possibly cv-qualified) POD class type (or array thereof) containing (directly or indirectly) a member of a const-qualified type, the program is ill-formed.
In other words, if an object of a non-POD class type does not appear in an initializer list, the compiler interprets this as if the object had appeared with its default constructor being called.
Also, note that other types (i.e. primitives and POD types) are not initialized, which is different from what you indicated in your question. Global objects are zero-initialized, but the same isn't true for objects on the stack. Here is a small program I put together to test this:
#include <iostream>
class T
{
public:
T() {}
void put(std::ostream &out)
{
out << "a = " << a << std::endl;
out << "b = " << b << std::endl;
out << "c = " << c << std::endl;
}
private:
int a;
int b;
int c;
};
T t2;
int main()
{
T t;
t.put(std::cout);
t2.put(std::cout);
return 0;
}
Compiling with g++ 4.5.2, I got the following output:
a = 8601256
b = 3
c = 2130567168
a = 0
b = 0
c = 0

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.