I have this snippet of code which seems to work well:
class foo{/* some member variables and functions*/};
void do_somthing(foo x={}){}
int main(){
do_somthing();
}
I used to use void do_somthing(foo x=foo()){} to default the x argument but I see this way ={} in some book or online example(can not remember). Is it totally ok to use it? Is there any difference between the two methods?
foo x=foo() is copy initialization,
Initializes an object from another object
and foo() is value initialization.
This is the initialization performed when a variable is constructed with an empty initializer.
foo x={} is aggregate initialization.
Initializes an aggregate from braced-init-list
If the number of initializer clauses is less than the number of
members and bases (since C++17) or initializer list is completely
empty, the remaining members and bases (since C++17) are initialized
by their default initializers, if provided in the class definition,
and otherwise (since C++14) by empty lists, which performs
value-initialization.
So the result is the same in this case (both value-initialized).
And the effects of value initialization in this case are:
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
Finally the effects of zero initialization in this case are:
If T is a scalar type, the object's initial value is the integral
constant zero explicitly 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.
Related
These two lines from cppreference
What is the difference between these two statements ? I don't see any difference
until c++14
If the braced-init-list is empty and T is a class type with a default
constructor, value-initialization is performed. Otherwise, if T is an
aggregate type, aggregate initialization is performed.
since c++14
If T is an aggregate type, aggregate initialization is performed.
Otherwise, if the braced-init-list is empty and T is a class type with
a default constructor, value-initialization is performed.
The difference is which one happens when both conditions apply: if T is an aggregate class (as opposed to an array), which certainly has a default constructor, and the braced-init-list is empty. Of course, to understand why that matters, we then have to distinguish value initialization from aggregate initialization from an empty list.
Value initialization zero-initializes the object and then default-initializes it, which for an aggregate is default-initializing each of its members, so the value-initialization is member-wise (plus zeroing padding). Aggregate initialization initializes each member from {}, which is again value initialization for many types but is default initialization for members of class type with a user-provided default constructor. The difference can be seen in
struct A {A() {} int i;};
struct B {A a;}; // aggregate
B b{}; // i is 0 in C++11, uninitialized in C++14
B b2=B(); // i is 0 in both versions
In C++14 only, aggregates can have default member initializers; that can't contribute to a difference in behavior between the two language versions, of course, but it doesn't behave differently between these two rules anyway (since it replaces only the common default initialization).
The difference is the sequence of checking, so the aggregate type checking is taking place at the first place, and only then the rest.
I have a class with an array member that I would like to initialize to all zeros.
class X
{
private:
int m_array[10];
};
For a local variable, there is a straightforward way to zero-initialize (see here):
int myArray[10] = {};
Also, the class member m_array clearly needs to be initialized, as default-initializing ints will just leave random garbage, as explained here.
However, I can see two ways of doing this for a member array:
With parentheses:
public:
X()
: m_array()
{}
With braces:
public:
X()
: m_array{}
{}
Are both correct? Is there any difference between the two in C++11?
Initialising any member with () performs value initialisation.
Initialising any class type with a default constructor with {} performs value initialisation.
Initialising any other aggregate type (including arrays) with {} performs list initialisation, and is equivalent to initialising each of the aggregate's members with {}.
Initialising any reference type with {} constructs a temporary object, which is initialised from {}, and binds the reference to that temporary.
Initialising any other type with {} performs value initialisation.
Therefore, for pretty much all types, initialisation from {} will give the same result as value initialisation. You cannot have arrays of references, so those cannot be an exception. You might be able to construct arrays of aggregate class types without a default constructor, but compilers are not in agreement on the exact rules. But to get back to your question, all these corner cases do not really matter for you: for your specific array element type, they have the exact same effect.
The types of initialization can be kind of tedious to go through, but in this case it is trivial. For:
public:
X()
: m_array()
{}
since the expression-list between the parentheses are empty, value-initialization occurs. Similarly for:
public:
X()
: m_array{}
{}
list-initialization occurs, and subsequently value-initialization since the brace-init-list is empty.
To give a more comprehensive answer, let's go through §8.5 of N4140.
If no initializer is specified for an object, the object is
default-initialized. When storage for an object with automatic or
dynamic storage duration is obtained, the object has an indeterminate
value, and if no initialization is performed for the object, that
object retains an indeterminate value until that value is replaced
(5.17).
This indeterminate value is what you refer to as garbage values.
To zero-initialize an object or reference of type T means:
— if T is an array type, each element is zero-initialized
To value-initialize an object of type T means:
— if T is a (possibly cv-qualified) class type ... then the object is default-initialized; ...
— if T is an array type, then each element is value-initialized;
— otherwise, the object is zero-initialized.
The semantics of initializers are as follows. ...
— If the initializer is a (non-parenthesized) braced-init-list, the object or reference is list-initialized (8.5.4).
— If the initializer is (), the object is value-initialized.
So far it's clear that value initialization will make each element of the array zero since int is not a class type. But we have not yet covered list initialization and aggregate initialization, since an array is an aggregate.
§8.5.4:
List-initialization of an object or reference of type T is defined as follows:
— If T is an aggregate, aggregate initialization is performed (8.5.1).
And back to §8.5.1:
If there are fewer initializer-clauses in the list than there
are members in the aggregate, then each member not explicitly
initialized shall be initialized from its brace-or-equal-initializer
or, if there is no brace-or-equal-initializer, from an empty
initializer list (8.5.4).
And we end with §8.5.4 again:
List-initialization of an object or reference of type T is defined as follows:
— Otherwise, if the initializer list has no elements, the object is value-initialized.
Since traversing the (draft) standard can take breath out of you, I recommend cppreference as it breaks it down pretty good.
Relevant links:
cppreference:
aggregate initialization
value initialization
Draft standard:
N4140
Parentheses work in C++98, and are calling for zero initialization, which is what you want. I verified on gcc 4.3. Edit: removed incorrect statement about C++11. I also confirmed that empty braces perform empty-list-initialization using clang 3.4 with -std=c++11.
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.
For some reason I had this idea that C and C++ worked like this:
int foo[10] = {57};
for (int i=0; i<10; ++i)
assert (foo[i] == 57);
Turns out the remaining ints are initialized to 0, not 57. Where did I get this idea? Was this true at one point? Was it ever true for structure initializer lists? When did arrays and structures neatly and correctly start initializing themselves to 0 values when assigned to = {} and = {0}? I always thought they'd initialize to garbage unless explicitly told otherwise.
It's been this way forever, as long as initializers existed. C89 says:
If there are fewer initializers in a list than there are members of an
aggregate, the remainder of the aggregate shall be initialized
implicitly the same as objects that have static storage duration.
Where did I get this idea?
It's apparently a relatively common misconception, as I've heard the same thing from a number of other people recently. Perhaps you picked it up from somebody else with this wrong idea, or perhaps the idea is just 'intuitive'.
{} Initialization has worked as it does at least as far back as C89. I'm not aware of it ever working differently, or any compilers that ever did it differently.
For initializer lists when initializing an aggregate type (like an array):
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). — Aggregates [dcl.init.aggr] 8.5.1p7
In C++ terms, when you use an empty initializer the object is value-initialized.
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.
— Initializers [dcl.init] 8.5p7
If I declare a class with default constructor and define a instance of this class with initializer list as below, will the default constructor be called for this definition? And why been called or not been called?
class Sample
{
// this should be any aggregate type in c++
};
int main()
{
Sample s = {0};
return 0;
}
In C++03, only aggregate classes may be initialized with curly braces, and an aggregate class may not have a user defined constructor
In C++0x, this syntax is supported for non aggregate types through initializer lists and calls the appropriate constructor (taking a std::initializer_list)
When you provide an brace enclosed initializer all the members of the class are copy-initialized from the corresponding expression of the brace enclosed initializer.
Such initialization is only valid for aggregates which cannot have user-declared constructors so the suppression of the compiler generated constructor is almost academic.
The Standard says ($8.5/14)
The semantics of initializers are as follows. The destination type is the type of the object or reference being initialized and the source type is the type of the initializer expression. The source type is not defined when the initializer is brace-enclosed or when it is a parenthesized list of expressions.
If the destination type is a (possibly cv-qualified) class type:
— If the class is an aggregate (8.5.1), and the initializer is a brace-enclosed list, see 8.5.1.
.
.
8.5.1/13
[Note: An aggregate array or an aggregate class may contain members of a class type with a user-declared constructor (12.1). Initialization of these aggregate objects is described in 12.6.1. ]
Also 12.6.1/2 says
When an aggregate (whether class or array) contains members of class type and is initialized by a brace-enclosed initializer-list (8.5.1), each such member is copy-initialized (see 8.5) by the corresponding assignment-expression. If there are fewer initializers in the initializer-list than members of the aggregate,
each member not explicitly initialized shall be value-initialized (8.5).
The corresponding constructor is called:
http://en.wikipedia.org/wiki/C%2B%2B0x#Initializer_lists
In C++ you may only initialize POD (plain old data) with ={0} (at least pre C++0x). So the default constructor will not be called because this won't compile.