Why is the move constructor involved here - c++

I have this piece of C++ code:
class Args {};
class MyClass {
public:
MyClass(Args& a) {}
MyClass(MyClass &&) = delete;
};
int main() {
Args a;
MyClass c1 = MyClass(a);
MyClass c2 = a;
MyClass c3(a);
return 0;
}
This does not compile because the construction of objects c1 and c2 seem to involve the class's move constructor:
error: use of deleted function ‘MyClass::MyClass(MyClass&&)’
It seems as if the compiler wants to create temporary object and then move them to c1 and c2. Why is this happening? Shouldn't all three statements just call the MyClass(Args& a) constructor?
On the other hand, if I do create the move constructor the program compiles fine and the move constructor is never called!!!

See copy elision:
Under the following circumstances, the compilers are permitted, but not required to omit the copy- and move- (since C++11) construction of class objects even if the copy/move (since C++11) constructor and the destructor have observable side-effects. This is an optimization: even when it takes place and the copy-/move-constructor is not called, it still must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed.
Since C++17:
They need not be present or accessible, as the language rules ensure that no copy/move operation takes place, even conceptually.

One major problem is this:
MyClass c1 = MyClass(a);
This creates a temporary object of type MyClass, and temporary types are rvalues. It then tries to either copy-construct c1 using the temporary object, or if you have a move-constructor then move-construct c1.
In C++11 and C++14, there will be no auto-generated copy-constructor for your class (because you have a user-defined (even if deleted) move-constructor), so only the deleted move-constructor is available. Well it would be available if it wasn't deleted, leading to your error.

Why is this happening? Shouldn't all three statements just call the MyClass(Args& a) constructor?
For both MyClass c1 = MyClass(a); and MyClass c2 = a;, temporary MyClass will be constructed at first by the constructor MyClass::MyClass(Args& a), then used to copy-initialize the object c1 and c2. The constructed temporaries are rvalues, that means the move-constructor will be selected for the copy initialization.
On the other hand, if I do create the move constructor the program compiles fine and the move constructor is never called!!!
The reason is copy elision; the copy/move operation is omitted here, results in the fact that MyClass::MyClass(Args& a) is used to initialize the object c1 and c2 directly.
The rule about copy elision changed since C++17. Note that pre-C++17, copy elision is not guaranteed. And for non-guaranteed copy elision, even when the copy/move operation is omitted the copy/move constructor still needs to be present and accessible.
This is an optimization: even when it takes place and the copy-/move-constructor is not called, it still must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed.
After C++17 your code would work fine. For guaranteed copy elision, copy/move constructor is not required to be present or accessible.
Under the following circumstances, the compilers are required to omit
the copy- and move- construction of class objects even if the
copy/move constructor and the destructor have observable side-effects.
They need not be present or accessible, as the language rules ensure
that no copy/move operation takes place, even conceptually:
In initialization, if the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the
class of the destination, the initializer expression is used to
initialize the destination object:
T x = T(T(T())); // only one call to default constructor of T, to initialize x

Related

Constructor initializer list is not calling copy constructor [duplicate]

This question already has answers here:
What are copy elision and return value optimization?
(5 answers)
Closed 4 years ago.
So I was learning about constructor initializer list and I wrote the following code :
class Mango
{
public:
Mango(){cout<<"Mango::ctor()";}
Mango(const Mango& other){cout<<"Mango::copy_ctor()";}
};
class Box
{
public:
Box() : mango(Mango()) //**doesn't call copy constructor**
{
}
Mango mango;
};
int main()
{
Box box;
return 0;
}
I used g++ compiler for this. Its calling the constructor not copy constructor. It should call copy constructor right because I am creating an object to create another object? What's the issue here and what does standard says about it?
Because of copy elision, the copy-construction is omitted here. This behavior is guaranteed from C++17. Before C++17 it's not mandatory; the compilers are permitted, but not required to perform copy elision1.
Under the following circumstances, the compilers are required to omit the copy- and move- construction of class objects even if the copy/move constructor and the destructor have observable side-effects. They need not be present or accessible, as the language rules ensure that no copy/move operation takes place, even conceptually:
In initialization, if the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object:
T x = T(T(T())); // only one call to default constructor of T, to initialize x
That means, mango will initialized by the default constructor directly.
[1] In fact most implementations would also perform copy elision before C++17. With Gcc you can try with -fno-elide-constructors option in pre-C++17 mode to disable copy elision.
As songyuanyao already explained so well in his excellent answer, the compiler is permitted to optimize away the invocation of the copy constructor in a number of cases.
Still, I'd like to address a different part of your question, just in case there is some misunderstanding:
I used g++ compiler for this. Its calling the constructor not copy constructor. It should call copy constructor right because I am creating an object to create another object? What's the issue here and what does standard says about it?
Your copy constructor needs to be provided with a (const Mango& other) in order to be invoked. If the compiler were to not perform copy elision, you would first see a call to the "normal" constructor "Mango::ctor()" followed by a call to the copy-constructor "Mango::copy_ctor()". Copy elision merely optimizes away the unnecessary copy-constructor call - the constructor for the object needs to be called in either way in order to get the Mango() object in Box() : mango(Mango())

Why are implicitly and explicitly deleted move constructors treated differently?

What is the rationale behind the different treatment of implicitly and explicitly deleted move constructors in the C++11 standard, with respect to the implicit generation of move constructors of containing/inheriting classes?
Do C++14/C++17 change anything? (Except DR1402 in C++14)
Note: I understand what is happening, I understand that it is according to the C++11 standard's rules, I'm interested in the rationale for these rules that imply this behavior (please make sure not to simply restate that it is the way it is because the standard says so).
Assume a class ExplicitDelete with an explicitly deleted move ctor and an explicitly defaulted copy ctor. This class isn't move constructible even though a compatible copy ctor is available, because overload resolution chooses the move constructor and fails at compile time due to its deletion.
Assume a class ImplicitDelete which either contains or inherits from ExplicitDelete and does nothing else. This class will have its move ctor implicitly declared as deleted due to C++11 move ctor rules. However, this class will still be move constructible via its copy ctor. (Does this last statement have to do with resolution of DR1402?)
Then a class Implicit containing/inheriting from ImplicitDelete will have a perfectly fine implicit move constructor generated, that calls ImplicitDelete's copy ctor.
So what is the rationale behind allowing Implicit to be able to move implicitly and ImplicitDelete not to be able to move implicitly?
In practice, if Implicit and ImplicitDelete have some heavy-duty movable members (think vector<string>), I see no reason that Implicit should be vastly superior to ImplicitDelete in move performance. ImplicitDelete could still copy ExplicitDelete from its implicit move ctor—just like Implicit does with ImplicitDelete.
To me, this behavior seems inconsistent. I'd find it more consistent if either of these two things happened:
The compiler treats both the implicitly and explicitly deleted move ctors the same:
ImplicitDelete becomes not move-constructible, just like ExplicitDelete
ImplicitDelete's deleted move ctor leads to a deleted implicit move ctor in Implicit (in the same way that ExplicitDelete does that to ImplicitDelete)
Implicit becomes not move-constructible
Compilation of the std::move line utterly fails in my code sample
Or, the compiler falls back to copy ctor also for ExplicitDelete:
ExplicitDelete's copy constructor is called in all moves, just like for ImplicitDelete
ImplicitDelete gets a proper implicit move ctor
(Implicit is unchanged in this scenario)
The output of the code sample indicates that the Explicit member is always moved.
Here's the fully working example:
#include <utility>
#include <iostream>
using namespace std;
struct Explicit {
// prints whether the containing class's move or copy constructor was called
// in practice this would be the expensive vector<string>
string owner;
Explicit(string owner) : owner(owner) {};
Explicit(const Explicit& o) { cout << o.owner << " is actually copying\n"; }
Explicit(Explicit&& o) noexcept { cout << o.owner << " is moving\n"; }
};
struct ExplicitDelete {
ExplicitDelete() = default;
ExplicitDelete(const ExplicitDelete&) = default;
ExplicitDelete(ExplicitDelete&&) noexcept = delete;
};
struct ImplicitDelete : ExplicitDelete {
Explicit exp{"ImplicitDelete"};
};
struct Implicit : ImplicitDelete {
Explicit exp{"Implicit"};
};
int main() {
ImplicitDelete id1;
ImplicitDelete id2(move(id1)); // expect copy call
Implicit i1;
Implicit i2(move(i1)); // expect 1x ImplicitDelete's copy and 1x Implicit's move
return 0;
}
So what is the rationale behind allowing Implicit to be able to move implicitly and ImplicitDelete not to be able to move implicitly?
The rationale would be this: the case you describe does not make sense.
See, all of this started because of ExplicitDelete. By your definition, this class has an explicitly deleted move constructor, but a defaulted copy constructor.
There are immobile types, with neither copy nor move. There are move-only types. And there are copyable types.
But a type which can be copied but has an explicitly deleted move constructor? I would say that such a class is a contradiction.
Here are the three facts, as I see it:
Explicitly deleting a move constructor is supposed to mean you can't move it.
Explicitly defaulting a copy constructor is supposed to mean you can copy it (for the purposes of this conversation, of course. I know you can still do things that make the explicit default deleted instead).
If a type can be copied, it can be moved. That's why the rule about implicitly deleted move constructors not participating in overload resolution exists. Therefore, movement is a proper subset of copying.
The behavior of C++ in this instance is inconsistent because your code is contradictory. You want your type to be copyable but not moveable; C++ does not allow that, so it behaves oddly.
Look at what happens when you remove the contradiction. If you explicitly delete the copy constructor in ExplicitDelete, everything makes sense again. ImplicitDelete's copy/move constructors are implicitly deleted, so it is immobile. And Implicit's copy/move constructors are implicitly deleted, so it too is immobile.
If you write contradictory code, C++ will not behave in an entirely legitimate fashion.

Unusual behavior of copy ctor

#include<iostream>
#include<string>
using namespace std;
class B;
class A {
public:
A(const A&) { //copy ctor
cout << "Copy ctor A\n";
}
A() {} //default ctor
A(const B&) { //lets call this conversion ctor
cout << "B to A conversion ctor\n";
}
};
class B {
public:
};
int main()
{
B b;
A a = b;
}
The above code prints
B to A conversion ctor.
But as per what I have discovered after looking around for a while, it should print
B to A conversion ctor
Copy ctor A
As first a temporary object of type A is created by conversion ctor and then that object is copied into a wherein copy ctor gets called. Also, when copy ctor is made private, statement A a = b; generates this error:
‘A::A(const A&)’ is private
which is obvious as to copy the temporary object into a copy ctor must be visible.
So my question is why copy ctor is not being eventually called as evident from the output(please correct if am leading wrong somewhere here) even though it is required to be accessible?
A a = b;
This form is called copy-initialization. The applicable rule states that in this case a temporary A object will be constructed from the B instance and that temporary will then be used to direct-initialize a.
However, the compiler is allowed to elide the copy as it is not necessary. Even though the elision is allowed, the class still needs to be copyable for the form to be valid.
You can see the result without elision by passing -fno-elide-constructors to GCC or Clang.
why copy ctor is not being eventually called as evident from the output
According to the standard, an implementation is allowed to omit the copy/move construction in certain criteria. In this case, a temporary A(constructed from b, and will be copied to a) 's construction is omitted.
$12.8/31 Copying and moving class objects [class.copy]
When certain criteria are met, an implementation is allowed to omit
the copy/move construction of a class object, even if the constructor
selected for the copy/move operation and/or the destructor for the
object have side effects.
And
(31.3) — when a temporary class object that has not been bound to a
reference (12.2) would be copied/moved to a class object with the same
type (ignoring cv-qualification), the copy/move operation can be
omitted by constructing the temporary object directly into the target
of the omitted copy/move

Understanding Default Move Constructor Definition

While reading about the move constructor from the current standard, I can see the following about this:
12.8 Copying and moving class objects
If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if
— X does not have a user-declared copy constructor,
— X does not have a user-declared copy assignment operator,
— X does not have a user-declared move assignment operator, and
— X does not have a user-declared destructor.
[ Note: When the move constructor is not implicitly declared or explicitly supplied, expressions that otherwise would have invoked the move constructor may instead invoke a copy constructor. — end note ]
I think note section clearly mentions that fall-back for default move constructor would be copy constructor. To understand this concept I wrote the small program to understand this concept.
#include<iostream>
struct handleclass {
public:
handleclass():length{0}, p{nullptr} {}
handleclass(size_t l):length{l},p{new int[length]} { }
~handleclass() { delete[] p; }
size_t length;
int* p;
};
handleclass function(void) {
handleclass x(10);
return x;
}
int main() {
handleclass y;
std::cout<<y.length<<std::endl;
y = function();
std::cout<<y.length<<std::endl;
handleclass a;
handleclass b(10);
a = std::move(b);
return 0;
}
Obviously this program is incorrect and would have undefined behaviour(terminate) due to the shallow copy of resources by two objects. But my focus is to understand the default move constructor generated and used in program. I hope this example make sense.
In the above program, in both cases where move constructor should be called, it appears to me that compiler is using default copy constructor.
Based on the above rule mentioned in the standard I think we should have got the compiler error as now program explicitly trying to call the move constructor and neither user has implemented nor compiler generates default(implicitly) as above rule does not satisfy?.
However this is getting compiled without any warning/error and running successfully. Could somebody explains about default(implicitly) move constructor concepts? Or I am missing something?.
You're forgetting about copy elision which means that y = function(); may not actually invoke any copy or move constructors; just the constructor for x and the assignment operator.
Some compilers let you disable copy elision, as mentioned on that thread.
I'm not sure what you mean by "in both cases where move constructor should be called". There are actually zero cases where move constructor should be called (your object does not have a move constructor), and one case where copy constructor could be called (the return statement) but may be elided.
You have two cases of assignment operator: y = function(); and a = std::move(b); . Again, since your class does not have a move-assignment operator, these will use the copy-assignment operator.
It would probably help your testing if you added code to your object to cout from within the copy constructor and move constructor.
Indeed, there is no implicit move constructor due to the user-declared destructor.
But there is an implicit copy constructor and copy-assignment operator; for historical reasons, the destructor doesn't inhibit those, although such behaviour is deprecated since (as you point out) it usually gives invalid copy semantics.
They can be used to copy both lvalues and rvalues, and so are used for the function return (which might be elided) and assignments in your test program. If you want to prevent that, then you'll have to delete those functions.
A move constructor and move assignment operator have not been implicitly declared because you have explicitly defined a destructor. A copy constructor and copy assignment operator have been implicitly declared though (although this behaviour is deprecated).
If a move constructor and move assignment operator have not been implicitly (or explicitly) declared it will fall back to using the copy equivalents.
As you are trying to call move-assignment it will fall back to using copy assignment instead.

What is the behaviour of compiler generated move constructor?

Does std::is_move_constructible<T>::value == true imply that T has a usable move constructor?
If so, what is the default behaviour of it?
Consider the following case:
struct foo {
int* ptr;
};
int main() {
{
std::cout << std::is_move_constructible<foo>::value << '\n';
foo f;
f.ptr = (int*)12;
foo f2(std::move(f));
std::cout << f.ptr << ' ' << f2.ptr << '\n';
}
return 0;
}
and the output is:
1
0000000C 0000000C
I thought that f.ptr should be nullptr.
So in this case,
Is f2 move constructed ?
If so, shouldn't the rvalue be invalidated?
How can I know if instances of a class can be properly move-constructed (invalidate the old one)?
(I'm using VS11.)
Update
The default behaviour of move constructor is same as a copy constructor, is it correct?
If it's true,
We always expect a move ctor to steal the resources of the moved-from object, while the default one does not behave as expected, so what's the point of having a default move ctor?
How can I know if a class has a custom move constructor (which can be guaranteed to behave properly)?
It seems that foo f2(std::move(f)); calls the copy ctor when I declared one, see:
struct foo {
int* ptr;
foo() {}
foo(const foo& other) {
std::cout << "copy constructed\n";
}
};
int main() {
{
std::cout << std::is_move_constructible<foo>::value << '\n';
foo f;
foo f2(std::move(f));
}
system("pause");
return 0;
}
Now the output is:
1
copy constructed
If foo has a move constructor, then wouldn't foo f2(std::move(f)) call it?
So now my questions is:
How to know if a class has a move ctor, and if it has one, how can I explicitly call it?
What I'm trying to do is…
template<typename T, bool has_move_ctor>
struct MoveAux;
template<typename T>
struct MoveAux<T, true> {
static void doMove(T* dest, T* src) {
new(dest) T(std::move(*src)); //move ctor
}
};
template<typename T>
struct MoveAux<T, false> {
static void doMove(T* dest, T* src) {
new(dest) T(*src); //copy ctor
src->~T();
}
};
template<typename T>
inline doMove(T* dest, T* src) {
MoveAux<T,/*a trait*/>::doMove(dest, src);
}
So I thought std::is_move_constructible<T>::value can be passed to the template, while now I see that this trait only cares if T t(T()) is a valid expression, it may call T::T(const T&).
Now assume that T is a custom class, then I want the above templates to behave like:
If I don't declare a move ctor, I want that template method calls the MoveAux<T,false>::doMove.
If I declared one, I need it calls to MoveAux<T,true>::doMove.
Is it possible to make this work?
does std::is_move_constructible<T>::value == true implies that T has a usable move constructor?
Either a move constructor or a copy constructor. Remember that the operation of copy construction satisfies all the requirements that are placed upon the operation move construction, and some more.
In Standard terms, a MoveConstructible object is one for which the evaluation of the expression:
T u = rv;
makes u equivalent to the value of rv before the construction; the state of rv after being moved-from is unspecified. But since it is unspecified, this means the state could even be identical to the one rv had before being moved from: In other words, u could be a copy of rv.
In fact, the Standard defines the CopyConstructible concept to be a refinement of the MoveConstructible concept (so everything which is CopyConstructible is also MoveConstructible, but not vice versa).
if so, what is the default behaviour of it?
The behavior of an implicitly generated move constructor is to perform a member-wise move of the data members of the type for which it is generated.
Per Parahgraph 12.8/15 of the C++11 Standard:
The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move
of its bases and members. [ Note: brace-or-equal-initializers of non-static data members are ignored. See
also the example in 12.6.2. —end note ]
Moreover:
1 - is f2 move constructed ?
Yes.
2 - if so, shouldn't the rvalue be invalidated?
Moving a pointer is the same as copying it. So no invalidation is going on, neither should it be going on. If you want a move constructor that leaves the moved-from object in a particular state (i.e. sets a pointer data member to nullptr), you have to write your own - or delegate this responsibility to some smart pointer class such as std::unique_ptr.
Notice, that the word "invalidated" is not quite correct here. Move constructors (as well as move assignment operators) are meant to leave the moved-from object in a valid (yet unspecified) state.
In other words, the class invariant needs to be respected - and it should be possible to invoke on a moved-from objects operations that do not have any precondition on its state (usually, destruction and assignment).
does std::is_move_constructible::value == true implies that T has a usable move constructor?
No. It states that you can take an rvalue expression of the object type and construct an object from it. Whether this uses the move constructor or the copy constructor is not relevant to this trait.
is f2 move constructed ?
Yes.
if so, shouldn't the rvalue be invalidated?
No. That's not how movement works.
how can I know if instances of a class can be properly move-constructed(invalidate the old one)?
That is not any definition of "properly move-constructed" that exists. If you want to "invalidate the old one", then you will have to do that yourself.
Move construction generally guarantees nothing about the state of the old object. It will be in a valid but undefined state. Such state very much can be "the same as it was before". Move construction for a pointer is the same as copying the pointer.
If you want to "invalidate" after a move, then you need to write your own move constructor that explicitly does that.
(I'm using VS11)
Then you have no compiler-generated move constructors at all. Not that it would matter, since the move and copy constructors for pointers both do the same thing.
the default behaviour of move constructor is same as a copy
constructor, is it correct? if it's true
No. It's wrong. It's true only for primitives. It's similar to that of copy constructor.
The default generated copy constructor calls the copy constructor of all its members in the declared order
But The default generated move constructor calls the move constructor of all its members in the declared order
Now the next question is, what is the copy/move constructor of the primitives ints floats pointers do?
Answer: They just copy the values (both copy and move constructor)
Note that Visual Studio 2012 / VC++11 does not support compiler generated move constructors; in fact, consider this quote from "C++11 Features in Visual C++ 11" blog post (emphasis mine):
Rvalue references v3.0 adds new rules to automatically generate move
constructors and move assignment operators under certain conditions.
This will not be implemented in VC11, which will continue to follow
VC10's behavior of never automatically generating move
constructors/move assignment operators.
With raw pointers, you have to define move constructors by yourself, manually clearing the old "moved-from" pointer:
class Foo
{
public:
// Move constructor
Foo(Foo&& other)
: m_ptr(other.m_ptr) // copy pointer value
{
// Clear out old "moved-from" pointer, to avoid dangling references
other.m_ptr = nullptr;
}
private:
int* m_ptr;
};
Instead, if you use a smart pointer like std::unique_ptr, move constructor is properly defined, and you can just call std::move:
class Foo
{
public:
// Move constructor
Foo(Foo&& other)
: m_ptr(std::move(other.m_ptr)) // move from other,
// old pointer automatically cleared
{
}
private:
std::unique_ptr<int> m_ptr;
};
With automatically generated move constructors, you don't have to define a custom move constructor explicitly, if member-wise move is OK for you.
n3376 12.8/15
The implicitly-defined copy/move constructor for a non-union class X performs a memberwise copy/move
of its bases and members.
Each base or non-static data
member is copied/moved in the manner appropriate to its type:
— if the member is an array, each element is direct-initialized with the corresponding subobject of x;
— if a member m has rvalue reference type T&&, it is direct-initialized with static_cast(x.m);
— otherwise, the base or member is direct-initialized with the corresponding base or member of x.
if foo has a move constructor, then wouldn't foo f2(std::move(f)) calls it?
You do not get the default move constructor when you supply your copy constructor. Add following line to get it ( and notice the change ).
foo(foo&& ifm)=default;