c++ - Direct and Copy Constructors - c++

class UnusualClass
{
int a;
public:
UnusualClass(int a){std::cout<<"Direct initialization"<<std::endl;}
UnusualClass(const UnusualClass &n){std::cout<<"Copy initialization"; }
};
int main ()
{
UnusualClass k1(5); //Direct initialization
UnusualClass k2=56; //Copy initialization
return 0;
}
Why does the compiler print out "Direct initialization" twice? I've done some research and found out that I might be getting copy constructor elision.
Is it possible to get two different outcomes in these two cases?
Also, when I use UnusualClass(const UnusualClass &n)=delete I get an error saying use of deleted function 'UnusualClass::UnusualClass(const UnusualClass&). Why would I get this error if it skips this constructor anyways?
I know I can get two different outcomes by using two constructors UnusualClass(int a); and UnusualClass(double b); but this trick doesn't seem quite right.

Copy initialization doesn't mean the copy constructor must be called.
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)
In the process of this copy initialization (i.e. UnusualClass k2=56;), UnusualClass::UnusualClass(int) will be selected for converting int to UnusualClass, so it's called at first. After that the converted UnusualClass is used to direct-initialize the object k2, so the copy constructor is required conceptually. Before C++17 even copy elision happens the copy constructor must be accessible, that's why when you make it delete you failed to compile. Since C++17 copy elision is guaranteed, and the copy constructor doesn't need to be accessible again.

This isn't copy initialization:
UnusualClass k2=56; // NOT Copy initialization
// for 56 is not of type UnusualClass
It will call the constructor:
UnusualClass(int a)
I think you meant:
UnusualClass k1(5); //Direct initialization
UnusualClass k2{k1}; //Copy initialization
UnusualClass k2 = k1; //Copy initialization
Note the type needed in copy initialization.
UnusualClass(const UnusualClass &n) // const reference to type UnusualClass
It should be the object's type which is UnusualClass, not int
UPDATE
I get an error saying use of deleted function
UnusualClass::UnusualClass(const UnusualClass&).
Why would I get this error if it skips this constructor anyways?
UnusualClass::UnusualClass(const UnusualClass&) = delete;
means:
From cppreference
Avoiding implicit generation of the copy constructor.
Thus, you will need to define your own copy constructor.
UPDATE 2
Refer more to #songyuanyao's answer for copy-initialization

UnusualClass k1(5); //Direct initialization
UnusualClass k2=56; //Copy initialization
In both of the above cases you are passing a integer, and the only constructor with integer argument is
UnusualClass(int a){std::cout<<"Direct initialization"<<std::endl;}
Hence the compiler print out "Direct initialization" twice.

The integer 56 is "implicitly" related to your constructor which expects an integer. If you want to disable this behavior, you can set the constructor to explicit.
explicit UnusualClass (int a) {...}
Disabling means that this will be detected as compiler error:
UnusualClass k = 56;

Related

Confusion in copy initialization and direct initialization

Consider simple statement (Taken from Is there a difference in C++ between copy initialization and direct initialization?):
A c2 = A();
This statement value-initializes a temporary and then copies that
value into c2 (Read 5.2.3/2 and 8.5/14). This of course will require a
non-explicit copy constructor (Read 8.5/14 and 12.3.1/3 and
13.3.1.3/1)
[Mind the bold sentence in above para] -> My question is why?
Now consider this code :
class B {};
struct A
{
A(B const&) {}
A(A const&) = delete;
//A(A const&); //delete above statement and uncomment this statement,
//and everything works, even though there in no body of copy constructor Oo
};
A a2 = B(); //error since there is no copy constructor oO
Why copy-initialization requires presence of copy constructor even though it's not needed sometime as presented in above code
Please please one more thing :
While direct initialization has all constructors available to call,
and in addition can do any implicit conversion it needs to match up
argument types, copy initialization can just set up one implicit
conversion sequence.
[Mind the bolding in the following para]
Doesn't that means direct initialization have access to all constructors and can perform implicit conversion sequence , while copy initialization all can do is perform implicit conversion sequence? . What I mean to ask is , implicit conversion in direct initialization is different from implicit conversion sequence in copy initialization ?
The rules for evaluating
A a1 = B(); // (1) copy-initialization
A a2 = {B()}; // (2) copy-initialization
A a3{B()}; // (3) direct-initialization
come from [dcl.init]/17:
— If the initializer is a (non-parenthesized) braced-init-list, the object or reference is list-initialized (8.5.4).
— [...]
— 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. [...]
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). [...] 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.
For both a2 and a3, the initializer is a braced-init-list, so we just do list-initialization. This ends up calling the B const& constructor.
For a1, the first sub-bullet doesn't apply - since the source type (B) is not the same or derived class of the destination type (A). So we go into the second sub-bullet point which involves considering conversion functions. There is one (A(B const&)) so we effectively rewrite the expression
A a1_new{A{B{}}};
Now typically, this extra copy will be elided. But you're explicitly prohibiting it, so the code cannot compile.
As to why the differentiation? I don't know. It seems like copy-initialization should simply be syntactic sugar for direct-initialization. In most cases, after all, it is...
(The following applies to C++11)To avoid rambling too much, A a2 = B(); has two stage : first, A temp object of type B is created when you write B(). Then, your function A(B const&) {} will not be invoked here with the role of directly initializing A because you use the copy-initialization syntax. If you want to invoke it to directly initialize A, you should write A a2(B()) instead, rather than using the copy-initialization syntax(the devil here is =). So, what's next? You get a temp object of type B, and you want to use it to initialize obj of type A, and now, you indirectly initialize A by converting B() to the type A. So your function A(B const&) {} is used as a type conversion rather than directly initializing A.
Because of conversion, a temp obj of type A is created, and then we need copy constructor to initialize a2 using that temporary obj.
In copy initialization, type-conversion-functions with explicit keyword can't be called. The design philosophy of explicit keyword is that you should directly invoke it. Combined with type-conversion-functions, those explicit attributed functions should not be used for implicit type conversion. In direct initialization, they can all be called even they are explicit.
To make things interesting, if you make your copy constructor and move constructor explicit, you can't even write T obj = T{}, but you can write T obj {T{}}. You can view copy and move ctor as a conversion too, only the destination and source type are of the same class.
I would like to provide you with further readings here. After that, read this question to know about copy-list-initialization and this question to learn about aggregate-types.

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.

In copy-initialization, is the call to the copy constructor explicit or implicit?

class AAA {
public:
explicit AAA(const AAA&) {}
AAA(int) {}
};
int main() {
AAA a = 1;
return 0;
}
In the above code, as I understand, though elided in most cases, the copy constructor is still semantically required to be called. My question is, is the call explicit or implicit? For a long time I have the conclusion in my mind that the call to AAA::AAA(int) is implicit but the call to the copy constructor is not. Today I accidentally got g++ to compile the above code and it reported error. (VC12 compiles OK.)
In section 8.5 of the standard:
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.
The bolded direct-initialize in the above quotes means the call to copy constructor is explicit, right? Is g++ wrong or my interpretation of the standard wrong?
Looks like this bug: g++ fails to call explicit constructors in the second step of copy initialization
g++ fails to compile the following code
struct X
{
X(int) {}
explicit X(X const &) {}
};
int main()
{
X x = 1; // error: no matching function for call to 'X::X(X)'
}
The second step of a copy initialization (see 8.5/16/6/2) is a
direct-initialization where explicit constructors shall be considered
as candidate functions.
Looks like copy constructor is never called . Only constructor is called . The following code may call copy constructor
AAA a = 1;
AAA ab = a;
Not sure why G++ is compiling it .

C++11 non-static member initializers and deleted copy constructor

I'm trying to compile the following simple code with GCC 4.7.2 (MinGW). Here I'm using C++11 feature - non-static member initializers:
#include <iostream>
using namespace std;
struct A
{
int var;
A()
{
cout << "A()\n";
}
A(int i)
{
cout << "A(int i)\n";
var = i;
}
A(const A&) = delete;
};
struct B
{
A a = 7;
};
int main()
{
B b;
cout << "b.a.var = " << b.a.var;
return 0;
}
This code doesn't compile because of deleted copy-constructor which isn't necessary here. Here are errors:
main.cpp:27:11: error: use of deleted function 'A::A(const A&)'
main.cpp:13:5: error: declared here
main.cpp: In constructor 'constexpr B::B()':
main.cpp:25:8: error: use of deleted function 'A::A(const A&)'
main.cpp:13:5: error: declared here
If I implement copy-constructor like this:
A(const A& a)
{
cout << "A(const A&)\n";
var = a.var;
}
Then code compiles fine and program gives me expected output:
A(int i)
b.a.var = 7
So it means that copy constructor is not used, but why I can't delete it?
Edit: Thanks for your answers. Copy or move constructor is required by standard if I'm using =. To fix this problem I need to implement move constructor or use direct initialization syntax A a{7}.
The initialiser for a gives you copy-initialization:
A a = 7;
For such a copy-initialization, where a user-defined conversion is required, the resulting initialisation is equivalent to:
A a(A(7));
That is, a temporary A is constructed and then passed to the copy constructor of your a object. This copying may be elided, but the copy constructor must be available nonetheless. In other words, the copying can only be elided if the copy would be possible in the first place. If you delete the copy constructor, the copying is impossible.
You'll have a better time with your deleted copy constructor if you do the following:
A a{7};
This does direct-initialization and no copy constructor is required.
Copy initialization is allowed to elide the copy but the copy constructor is mandated to be accessible by the standard.
Per Paragraph 12.2/14 of the C++11 Standard:
The initialization that occurs in the form
T x = a;
as well as in argument passing, function return, throwing an exception (15.1), handling an exception (15.3), and aggregate member initialization (8.5.1) is called copy-initialization. [ Note: Copy-initialization may invoke a move (12.8). —end note ]
The reason why your copy-initialization doesn't compile is that during copy-initialization a temporary object needs to be created (at least logically), and the object being initialized shall be constructed from it.
Now all the previous answers seem to focus just on copy-constructors, but the first problem here is the absence of a move-constructor. As long as you provide one, then it is true that the copy constructor is not necessary.
Alas, deleting the copy constructor prevents the generation of an implicit move constructor. Adding one explicitly would fix the problem:
struct A
{
int var;
A()
{
cout << "A()\n";
}
A(int i)
{
cout << "A(int i)\n";
var = i;
}
A(const A&) = delete;
// THIS MAKES IT WORK
A(A&& a)
{
cout << "A(A&&)\n`;
var = a.var;
}
};
Notice that when both the move-constructor and the copy-constructor are present, the move-constructor is preferred, because the temporary created for copy-initializing your object is an rvalue.
When a move-constructor is absent, the compiler can invoke the copy constructor to perform the initialization, because constant lvalue references can bind to rvalue references and copy is seen as an unoptimized move.
However, even though the compiler is allowed to elide the call to the move or the copy constructor, the semantics of the operation must still be checked. Per Paragraph 12.8/32 of the C++11 Standard:
When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. If overload resolution fails, or if the type of the first parameter of the selected constructor is not an rvalue reference to the object’s type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue. [ Note: This two-stage overload resolution must be performed regardless of whether copy elision will occur. It determines the constructor to be called if elision is not performed, and the selected constructor must be accessible even if the call is elided. —end note ] [...]
Therefore, an error is issued by the compiler if neither the move constructor nor the copy constructor are present.
If you want, however, you can direct-initialize your object rather than copy-initializing it. Just use the direct-initialization syntax instead:
struct B
{
A a{7};
};
This will make the move-constructor and copy-constructor unnecessary, because no temporary is created when direct-initializing an object.
This code doesn't compiles because of deleted copy-constructor which isn't necessary here
Sorry, but your copy constructor is necessary. Even though the copy can be optimised out, it must still be possible in the code. This mandated by the language.
So it means that copy constructor is not used, but whyI can't delete it?
In your case, copy constructor is used only for semantic-check as required by the Standard, it also needs to be accessible. Later on, the compiler optimizes the code, eliding the call to the copy-constructor, so it is not actually invoked.

Which constructor is invoked here?

In this code fragment, which constructor is actually called?
Vector v = getVector();
Vector has copy constructor, default constructor and assignment operator:
class Vector {
public:
...
Vector();
Vector(const Vector& other);
Vector& operator=(const Vector& other);
};
getVector returns by value.
Vector getVector();
Code uses C++03 standard.
Code fragment looks like it is supposed to call default constructor and then assignment operator, but I suspect that this declaration is another form of using copy constructor. Which is correct?
When = appears in an initialization, it calls the copy constructor. The general form is not exactly the same as calling the copy constructor directly though. In the statement T a = expr;, what happens is that if expr is of type T, the copy constructor is called. If expr is not of type T, then first an implicit conversion is done, if possible, then the copy constructor is called with that as an argument. If an implicit conversion is not possible, then the code is ill-formed.
Depending upon how getVector() is structured, the copy may be optimized away, and the object that was created inside the function is the same physical object that gets stored in v.
Assuming that you haven't done something pathological outside of the code you are showing, your declaration is a copy-initialization, and the second part of this rule applies:
13.3.1.3 Initialization by constructor [over.match.ctor]
1 When objects of class type are direct-initialized (8.5), or copy-initialized from an
expression of the same or a derived class type (8.5), overload resolution selects the
constructor. For direct-initialization, the candidate functions are all the constructors
of the class of the object being initialized. For copy-initialization, the candidate
functions are all the converting constructors (12.3.1) of that class. The argument
list is the expression-list within the parentheses of the initializer.
For a simple test case, see Eli Bendersky's post, here: http://eli.thegreenplace.net/2003/07/23/variable-initialization-in-c/
Always remember the rule:
Whenever, an object is being created and given some value in the same single statement then it is never an assignment.
To add Further,
Case 1:
Vector v1;
Vector v(v1);
Case 2:
Vector v = getVector();
In the above two formats Case 1 is Direct Initialization while Case 2 is known as Copy Initialization.
How does Copy Initialization work?
Copy initialization constructs an implicit conversion sequence: It tries to convert return value of getVector() to an object of type Vector. It can then copy the created object into the object being initialized, So it needs a accessible copy constructor.
The copy constructor actually gets elided in this case (check this) and just the default constructor ends up getting called
EDIT:
The constructor is only sometimes elided, as per Benjamin's answer. For some reason I read that as you calling the constructor directly.