I'm trying to understand what the following line does:
BStats stats = BStats();
The struct is defined as follows:
struct BStats
{
unsigned a;
unsigned b;
BStats& operator+=(const BStats& rhs)
{
this->a += rhs.a;
this->b += rhs.b;
return *this;
}
};
But I have no idea about what this line does. Is it calling the default constructor?
The expression BStats() is described in the standard in 5.2.3/2:
The expression T(), where T is a simple-type-specifier (7.1.5.2) for a non-array complete object type or the (possibly cv-qualified) void type, creates an rvalue of the specified type, which is value-initialized.
That is, the expression creates an rvalue of Bstats type that is value-initialized. In your particular case, value-initialization means that the two members of the BStats struct will be set to zero.
Note that this is different than the behavior of calling the default-constructor that is mentioned in other answers, as the default constructor will not guarantee that the members are set to 0.
Just like any class, a struct has a default constructor automatically created by the compiler. In your case, BStats() simply calls the default constructor, although the explicit call is useless.
In C++ Classes and Structs are almost the same (the difference is that C++ structs are classes with public as the default attribute where a class's is private) so it's like calling a 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?
Consider the following two classes:
class B
{
public:
B() { }
B(const B& b) = delete; //Move ctor not implicitly declared
};
class A
{
public:
A() { }
operator B()
{
return B();
}
};
I can see why this code compiles fine:
A a;
B b = a;
Following the rules of copy-initialization, the object "a" gets converted to a prvalue of type B and since in C++17 the copy constructor is not needed anymore there's no error:
If T is a class type, and the cv-unqualified version of the type of
other is not T or derived from T, or if T is non-class type, but the
type of other is a class type, user-defined conversion sequences that
can convert from the type of other to T (or to a type derived from T
if T is a class type and a conversion function is available) are
examined and the best one is selected through overload resolution. The
result of the conversion, which is a prvalue temporary (until
C++17)prvalue expression (since C++17) if a converting constructor was
used, is then used to direct-initialize the object. The last step is
usually optimized out and the result of the conversion is constructed
directly in the memory allocated for the target object, but the
appropriate constructor (move or copy) is required to be accessible
even though it's not used. (until C++17)
However why does this direct list-initialization compile too?
A a;
B b{ a };
I couldn't find any wording in the list-initialization stating the compiler should attempt to convert A into B in this case. Only that overload resolution on constructors is considered:
If the previous stage does not produce a match, all constructors of T
participate in overload resolution against the set of arguments that
consists of the elements of the braced-init-list, with the restriction
that only non-narrowing conversions are allowed
However in this case the copy constructor is deleted, so shouldn't it not be selected by overload resolution?
This is CWG 2327. You're correct as far as the standard goes, but some compilers additionally consider conversion functions in this context as well - because it really makes sense to.
For example I guess I understand what list-initialization in context of direct-initialization (vs copy-) mean - int x{} vs int x = {} basically.
But on cppreference I found this:
When an object of class type is copy-initialized from an object of the same or derived class type, or default-initialized in a copy-initialization context, the candidate functions are all converting constructors of the class being initialized. The argument list is the expression of the initializer.
I guess I understand why candidates are converting constructors for the first case, but not for the second. I mean, I can't write something like MyClass x = MyClass, and = MyClass() would be a value-initialization, and = MyClass(args...) would be a direct-initilization.
And even if such a construct existed, I don't see why a temporary MyClass object 'construction' should include specifically all converting constructors.
(And x is not something that's talked about here as I see, because it's definitely copy-constructed, not default-constructed.)
So I guess I'm confused with the terms here.
This wording is added in the paper P0398R0, which is intented to describe the following case:
Z c = {};
for non-aggregate Z.
This means, when you initialize an object, it is not default constructed and then copied using assignment operator, but always immediately constructed from the parameters given in the initialization. Therefore it uses only the conversions, when you use the assignment notation.
Example:
Given the class:
class MyClass
{
MyClass();
MyClass(int);
};
The following statement are only calling the MyClass::MyClass(int) and no default constructor.
MyClass obj = 10;
MyClass obj{10};
MyClass obj = {10};
I'm searching for answers but i can't find any relevant information on this. Let's take the example:
class MyClass
{
//member functions and variables
};
void foo(int pivot,...)
{
va_list arguments;
va_start(arguments,pivot);
//va_arg(arguments,???)
va_end(arguments);
}
void bar()
{
MyClass a;
MyClass * b = &a;
const MyClass & c = a;
foo(0,a,b,c);
}
How are the arguments a,b and c passed? By value , or by reference and how to ask for them using va_arg? What about the constructors/destructor for MyClass? Where in the c++ standard is this kind of behavior specified?
You should never use user-defined types in var-arg function. Use C++11 variadic templates.
If your class is not pod-type - it's unspecified by standard, thanks to Vaughn Cato for remark
n3337 5.2.2/7
Passing a potentially-evaluated argument of class type (Clause 9) having a nontrivial
copy constructor, a non-trivial move constructor, or a non-trivial destructor, with no corresponding
parameter, is conditionally-supported with implementation-defined semantics.
Else, you can and it will be correct, but you shouln't.
By value. But beware, if MyClass is not a POD, the program
has undefined behavior (C++03, §5.2.2/7), or if MyClass has
a non-trivial copy constructor, move constructor or destructor,
the operation is conditionally supported, with implementation
defined semantics (C++11, §5.2.2/7).
In your example, passing a and passing c are exactly
identical operations (except that c cannot be bound to
a non-const reference, but that's not an issue here, since
varargs are all pass by value). Thus, when calling foo, you
pass 0, a copy of a, a copy of the pointer b, and a copy
of a. In order to access them in foo, you need to declare
the types in va_arg as int, MyClass, MyClass* and
MyClass.
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?