What does 'default-initialization in copy-initialization context' mean in C++? - c++

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};

Related

C++ direct list initialization with deleted default constructor

I have the following class definition. I included a private x to make sure it is not an aggregate.
class A {
private:
int x;
public:
A() = delete;
A(std::initializer_list<int>) { printf("init\n"); }
};
Now if I initialize this object with A a = A{}, it will say A::A() is deleted. I guess it is trying to call A::A() but it is deleted. If I comment out that line, so A::A() is automatically generated. Then if I run this code, I could see that it is calling A::A(std::initializer_list<int>)!
And more confusing, if I define A() = default, the initialization calls A::A() again.
Can anyone point me to the right direction of understading this behavior? Thanks!
I'm using G++ 6.3.0 with flag -std=c++17.
The behaviour is correct.
First of all:
// Calling
A a = A{}; // equals A a = A();,
which was a convenience uniformization of the list-initialization idiom.
Refer to: https://stackoverflow.com/a/9020606/3754223
Case A:
class A {
private:
int x;
public:
**A() = delete;**
A(std::initializer_list<int>) { printf("init\n"); }
};
As said above, A a = A{} will be ... = A(), which is deleted.
Case B:
class A {
private:
int x;
public:
A(std::initializer_list<int>) { printf("init\n"); }
};
In this case, there's no default constructor provided, since you have your initializer-list constructor be defined. Consequently {} is converted to an empty initializer list implicitely!
Case C:
class A {
private:
int x;
public:
**A() = default;**
A(std::initializer_list<int>) { printf("init\n"); }
};
In this case the empty default constructor is available and A{} is converted back to A(), causing it to be called.
Please refer to the below pages and get a thorough read into it.
http://en.cppreference.com/w/cpp/utility/initializer_list
http://en.cppreference.com/w/cpp/language/value_initialization
Finally:
Using:
A a = {{}};
Would cause that ALWAYS the initializer-list constructor is called, as the outer-curly-brackets denoted the init-list, and the inner a zero-constructed element. -> Non-empty initializer-list... (See stackoverflow link above again!)
And btw, I cannot understand why the question is downvoted, considering that this is a really tricky part...
You have lots of cases listed, so let us go over them one by one.
A() = delete;
A(std::initializer_list<int>) { ... }
Writing A a = A{}; will indeed try to call the deleted default constructor, and your code fails to compile.
What you have above is list initialization, and because the braced-init-list is empty, value initialization will be performed. This selects the deleted default constructor, which makes your code ill-formed.
If I comment out that line, so A::A() is automatically generated
No, this is not the case. There is no implicitly declared default constructor due to the presence of the user provided constructor that takes an initializer_list. Now, A a = A{}; will call the initializer_list constructor, and in this case the initializer_list will be empty.
if I define A() = default, the initialization calls A::A() again
This behaves the same as the first case, except the default constructor is explicitly defaulted instead of deleted, so your code compiles.
Finally,
I included a private x to make sure it is not an aggregate
There's no need for this, defining a constructor makes A a non-aggregate.
List initialization follows a very specific ordering, as laid out in [dcl.init.list]/3. The two relevant bullet points and their relative ordering are highlighted:
List-initialization of an object or reference of type T is defined as follows:
— If T is an aggregate class and [...]
— Otherwise, if T is a character array and [...]
— Otherwise, if T is an aggregate, [...]
— Otherwise, 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 a specialization of std::initializer_list<E>, [...]
— Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7).
— Otherwise, if the initializer list has a single element of type E and [...]
— Otherwise, if T is a reference type, [...]
— Otherwise, if T is an enumeration with a fixed underlying type (7.2), [...]
— Otherwise, if the initializer list has no elements, the object is value-initialized.
— Otherwise, the program is ill-formed.
An empty initializer list for class types with default constructors is a special case that precedes normal constructor resolution. Your class isn't an aggregate (since it has a user-provided constructor), so A{} will attempt to value-initialize A if A has a default constructor. It doesn't matter if that default constructor is accessible, it only matters if it exists. If it exists and is inaccessible (your first case), that's ill-formed. If it exists and is accessible (your third case), then it's invoked. Only if your class does not have a default constructor will the constructors be enumerated, in which case the A(initializer_list<int> ) constructor will be invoked with an empty initializer list (your second case).

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.

Move constructor is required even if it is not used. Why?

Why?! Why C++ requires the class to be movable even if it's not used!
For example:
#include <iostream>
using namespace std;
struct A {
const int idx;
// It could not be compileld if I comment out the next line and uncomment
// the line after the next but the moving constructor is NOT called anyway!
A(A&& a) : idx(a.idx) { cout<<"Moving constructor with idx="<<idx<<endl; }
// A(A&& a) = delete;
A(const int i) : idx(i) { cout<<"Constructor with idx="<<i<<endl; }
~A() { cout<<"Destructor with idx="<<idx<<endl; }
};
int main()
{
A a[2] = { 0, 1 };
return 0;
}
The output is (the move constructor is not called!):
Constructor with idx=0
Constructor with idx=1
Destructor with idx=1
Destructor with idx=0
The code can not be compiled if moving constructor is deleted ('use of deleted function ‘A::A(A&&)’'. But if the constructor is not deleted it is not used!
What a stupid restriction?
Note:
Why do I need it for? The practical meaning appears when I am trying to initialize an array of objects contains unique_ptr field.
For example:
// The array of this class can not be initialized!
class B {
unique_ptr<int> ref;
public:
B(int* ptr) : ref(ptr)
{ }
}
// The next class can not be even compiled!
class C {
B arrayOfB[2] = { NULL, NULL };
}
And it gets even worse if you are trying to use a vector of unique_ptr's.
Okay. Thanks a lot to everybody. There is big confusion with all those copying/moving constructors and array initialization. Actually the question was about the situation when the compiler requires a copying consrtuctor, may use a moving construcor and uses none of them.
So I'm going to create a new question a little bit later when I get a normal keyboard. I'll provide the link here.
P.S. I've created more specific and clear question - welcome to discuss it!
A a[2] = { 0, 1 };
Conceptually, this creates two temporary A objects, A(0) and A(1), and moves or copies them to initialise the array a; so a move or copy constructor is required.
As an optimisation, the move or copy is allowed to be elided, which is why your program doesn't appear to use the move constructor. But there must still be a suitable constructor, even if its use is elided.
A a[2] = { 0, 1 };
This is aggregate initialization. §8.5.1 [dcl.init.aggr]/p2 of the standard provides that
When an aggregate is initialized by an initializer list, as specified in 8.5.4, the elements of the initializer list are taken as initializers for the members of the aggregate, in increasing subscript or member order. Each member is copy-initialized from the corresponding initializer-clause.
§8.5 [dcl.init]/p16, in turn, describes the semantics of copy initialization of class type objects:
[...]
If the destination type is a (possibly cv-qualified) class type:
If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source
type is the same class as, or a derived class of, the class of the
destination, constructors are considered. The applicable constructors
are enumerated (13.3.1.3), and the best one is chosen through overload
resolution (13.3). The constructor so selected is called to initialize
the object, with the initializer expression or expression-list as its
argument(s). If no constructor applies, or the overload resolution is
ambiguous, the initialization is ill-formed.
Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source
type to the destination type or (when a conversion function is used)
to a derived class thereof are enumerated as described in 13.3.1.4,
and the best one is chosen through overload resolution (13.3). If the
conversion cannot be done or is ambiguous, the initialization is
ill-formed. The function selected is called with the initializer
expression as its argument; if the function is a constructor, the call
initializes a temporary of the cv-unqualified version of the
destination type. The temporary is a prvalue. The result of the call
(which is the temporary for the constructor case) is then used to
direct-initialize, according to the rules above, the object that is
the destination of the copy-initialization. In certain cases, an
implementation is permitted to eliminate the copying inherent in this
direct-initialization by constructing the intermediate result directly
into the object being initialized; see 12.2, 12.8.
Since 0 and 1 are ints, not As, the copy initialization here falls under the second clause. The compiler find a user-defined conversion from int to A in your A::A(int) constructor, calls it to construct a temporary of type A, then performs direct initialization using that temporary. This, in turn, means the compiler is required to perform overload resolution for a constructor taking a temporary of type A, which selects your deleted move constructor, which renders the program ill-formed.

Using struct name as a function

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.

Can a member struct be zero-init from the constructor initializer list without calling memset?

Let's say I have the following structure declaration (simple struct with no constructor).
struct Foo
{
int x;
int y;
int z;
char szData[DATA_SIZE];
};
Now let's say this struct is a member of a C++ class as follows:
class CFoobar
{
Foo _foo;
public:
CFoobar();
};
If I declare CFoobar's constructor as follows:
CFoobar::CFoobar()
{
printf("_foo = {%d, %d, %d}\n", _foo.x, _foo.y,_foo.z);
for (int x = 0; x < 100; x++)
printf("%d\n", _foo.szData[x]);
}
As you would expect, when CFoobar's constructor runs, garbage data gets printed out Obviously, the easy fix is to memset or ZeroMemory &_foo. It's what I've always done...
However, I did notice that if add _foo to the constructor's initialization list with no parameters as follows:
CFoobar::CFoobar()
: _foo()
{
That this appears to zero-out the member variables of _foo. At least that was the case with g++ on linux.
Now here's my question: Is this standard C++, or is this compiler specific behavior?
If it's standard behavior, can someone quote me a reference from an official source? Any "gotchas" in regards to implicit zero-init behavior with more complicated structs and classes?
Yes, this is defined behaviour according to the standard. 12.6.2 [class.base.init] / 3 : "if the expression-list of the mem-initializer is omitted, the base class or member subobject is value-initialized."
Be warned, though, if Foo wasn't a POD-type but still had no user-declared constructor (e.g. it had a std::string type) then some very popular compilers would not correctly value-initialize it.
All compilers that I know of do correctly perform value-initialization of POD members when you use () as the initializer in a constructor initializer-list.
i find it hard to read the standard, but I found it I think:
To value-initialize an object of type T means:
if T is a non-union class type without a user-declared constructor, then every non-static data member and base-
class component of T is value-initialized
Value-initialization for such a class object may be implemented by zero-initializing the object and then calling the default constructor.
Section 8.5
It's the equivalent of float foo = float();
It will zero the object, even if the value representation is not all-bits-zero. I.e. it's even better than memset().