Implicit move constructor and assignment operator - c++

What does it mean that implicit move constructor does a member-wise move and implicit move assignment operator a member-wise assignment?
From https://en.cppreference.com/w/cpp/language/move_constructor:
For non-union class types (class and struct), the move constructor
performs full member-wise move of the object's bases and non-static
members, in their initialization order, using direct initialization
with an xvalue argument. If this satisfies the requirements of a
constexpr constructor, the generated move constructor is constexpr.
From https://en.cppreference.com/w/cpp/language/move_assignment:
For non-union class types (class and struct), the move assignment
operator performs full member-wise move assignment of the object's
direct bases and immediate non-static members, in their declaration
order, using built-in assignment for the scalars, memberwise
move-assignment for arrays, and move assignment operator for class
types (called non-virtually).
Will the implicit members look like this for the following exemplary class template:
template<class T>
class Holder {
public:
Holder(int size) : m_size(size) { m_data = new T[m_size]; }
Holder(Holder && other) :
m_size(std::move(other.m_size)),
m_data(std::move(other.m_data))
{}
Holder& operator=(Holder && other) {
if(this == &other) return *this;
m_data = std::move(other.m_data);
m_size = std::move(other.m_size);
return *this;
}
~Holder() { delete [] m_data; }
private:
T* m_data;
int m_size;
};
What's more, what will the std::move() in the above example transfer the resources?

If you look further down you linked page, you will see that your classes compiler generated move constructor (and move assignment operator) will actually be Trivial:
Trivial move constructor
The move constructor for class T is trivial if all of the following is true:
it is not user-provided (meaning, it is implicitly-defined or defaulted);
T has no virtual member functions;
T has no virtual base classes
the move constructor selected for every direct base of T is trivial;
the move constructor selected for every non-static class type (or array of class type) member of T is trivial;
A trivial move constructor is a constructor that performs the same action as the trivial copy constructor, that is, makes a copy of the
object representation as if by std::memmove. All data types
compatible with the C language (POD types) are trivially movable.
(Emphasis mine)
The two member variables are POD types and therefore are trivially movable. Since your class is not virtual and it holds no non-trivial members it is therefore trivial and all the data members will be copied. As mentioned in the comments, this will lead to double deleting your pointer and UB.
Since this is the case, you need to implement your move semantics properly, by taking ownership of the moved objects pointer and setting it to nullptr. Or better yet, just use std::vector or even std::unique_ptr.

Related

Is an empty constructor with initializer list considered trivial?

Is the following constructor considered trivial?
struct A
{
A() : a(nullptr) {}
private:
int* a;
};
These examples makes me a little confused. With c++11 this should also be possible:
struct A
{
private:
int* a{nullptr};
};
and a should be properly initialized to nullptr. Here I did not define the constructor, but it should have the same form as the first implementation. Is either of these classes considered trivial?
The purpose of me asking is if I can expect move/copy constructors and assignment operators being automatically generated.
Is the following constructor considered trivial?
A() : a(nullptr) {}
No, because it is user-defined.
struct A
{
private:
int* a{nullptr};
};
No, because it has brace initializer for a non-static member.
According to the standard (emphasis mine):
12.1 Constructors....
A default constructor for a class X is a constructor of class X that can be called without an argument. If
there is no user-declared constructor for class X, a constructor having no parameters is implicitly declared as defaulted (8.40).....
A default constructor is trivial if it is not user-provided and if:....
— no non-static data member of its class has a brace-or-equal-initializer, and....
Otherwise, the default constructor is non-trivial.
The purpose of me asking is if I can expect move/copy constructors and assignment operators being automatically generated.
As #M.M and #NicolBolas commented, generation of these constructors and operators are not impacted by existence of the trivial constructor.
The rules are a bit complicated and not very consistent.
The copy constructor is generated only if there is no explicitly declared one. (And it is deleted if the move constructor or the move assignment operator is declared.)
Similarly, the copy assignment operator is generated only if there is no explicitly declared one. (And again, it is deleted if the move constructor or the move assignment operator is declared.)
The move constructor is generated only if there are no explicitly declared move constructor, move operator, copy constructor, copy assignment and destructor.
The same rule is for the move assignment operator.

Can C++ classes with deleted methods be trivially copyable?

I want class B to inherit all but a few methods of class A (which is assumed to be trivially copyable), and still be trivially copyable. In C++11 I can delete methods. Take for example:
class A { // trivially copyable
// private stuff here
public:
A& operator += (const A&);
// other public stuff here
};
class B: public A {
public:
B& operator += (const A&) = delete;
};
Is B trivially copyable? I know there are issues regarding the deletion of special methods, but the compound assignment is not a special method (right?).
Yes, B is trivially copyable - regardless of what you do to non-special member functions.
N3337, §9/6:
A trivially copyable class is a class that: — has no non-trivial
copy constructors (12.8), — has no non-trivial move constructors
(12.8), — has no non-trivial copy assignment operators (13.5.3, 12.8),
— has no non-trivial move assignment operators (13.5.3, 12.8), and —
has a trivial destructor (12.4).
but the compound assignment is not a special method (right?)
No, it's not.
N3337, §12/1:
The default constructor (12.1), copy constructor and copy assignment
operator (12.8), move constructor and move assignment operator (12.8),
and destructor (12.4) are special member functions.
I think you're on the right track--if A is trivially copyable and B is derived from A and simply deletes some regular methods (or operators), B will be trivially copyable too.

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;

C++: autogenerated(default) copy costructor

If I declare a class like, (no dynamic memory allocation, no pointer):
class A{
int a,b;
public:
A();
A(int,int);
A& operator=(const A);
};
Is it safe not to declare a copy constructor? How does the default copy constructor looks like?
A& A::operator=(const A other)
{
a=other.a;
b=other.b;
return *this;
}
Even If I do not declare a copy constructor, the default one will be called when I call operator=()
EDIT:
the default destructor is:
A::~A(){}
so it's not needed here
The rule is that if you need to provide either:
copy constructor or
destructor or
copy assignment operator
then you probably need to provide all three of them. This rule is known as Rule of Three.
Is it safe not to declare a copy constructor?
It is safe.
Do you have to for your example case?
Not really. To be specific the rule of three governs that. Check the linked question for more details on that.
How does the default copy constructor looks like?
I gather this is asking, What does the default copy constructor do.
This is answered in:
C++03 Standard 12.8 Copying class objects:
Para 8:
The implicitly-defined copy constructor for class X performs a memberwise copy of its subobjects. The order of copying is the same as the order of initialization of bases and members in a user-defined constructor (see 12.6.2). Each subobject is copied in the manner appropriate to its type:
— if the subobject is of class type, the copy constructor for the class is used;
— if the subobject is an array, each element is copied, in the manner appropriate to the element type;
— if the subobject is of scalar type, the built-in assignment operator is used.
Virtual base class subobjects shall be copied only once by the implicitly-defined copy constructor (see 12.6.2).
Even If I not declare a copy constructor, the default one will be called when I call operator=()
A copy constructor is invoked only when a copy of the class object needs to be created. This involves copies of objects created while passing to or returning from functions.
Your copy assignment operator passes the object A by value, this pass by value is achieved by passing a copy of the object through copy constructor and hence the call to copy constructor.
To avoid the copy you need to pass by reference:
A& A::operator=(const A& other)
Good Read:
What's the difference between passing by reference vs. passing by value?
You seem to be confusing copy-constructors with copy-assignment operators. The implicitly generated copy-constructor will copy-construct each of the members:
A::A( A const& source )
: a( source.a )
, b( source.b )
{}
The implicitly generated copy-assignment operator will copy-assign each of the members:
A& A::operator =( A const& source )
{
a = source.a;
b = source.b;
return *this;
}
The copy-assignment operator as you defined it in your question takes an A by copy, so the copy-constructor will be called to create the argument passed to operator=. Note that the copy may be elided under certain circumstances.
Your class provides operator=. Per the rule of three (as #AlokSave says), you should then provide the copy constructor and destructor as well.
The question is, if you're happy with the default-supplied copy constructor, why aren't you happy with the default-supplied copy assignment operator? And if you are, just don't declare operator= and let the compiler generate the entire "rule of three" for you.
A::A(const A& other) != A& A::operator=(const A other)

Implicitly-declared Move-Operations do not fallback to Copy?

Do I read N3291 "12.8.(11/15/28) Copying and moving class objects class.copy]" correct that the implicitly-declared move constructor
does an element-wise move of all non-static data-members (probably via respectively defined T(T&&)
and if any non-static data-member can not be moved, the implicit move-constructor will be marked as deleted and not tried to be copied as a "fallback"? (yes, move is defined for built-in types, but actually is a copy).
and likewise the move-assign, using the respective T operator=(T&&) of the elements.
Example:
struct CopyOnly {
CopyOnly();
CopyOnly(const CopyOnly&);
}; // declaring a copy means no implicit move.
struct Question {
std::vector<int> data_;
CopyOnly copyOnly_;
};
The class Question
will have implicitly-declared copy-constructor and assign
will have implicitly-declared move-constructor and move-assign, but they will be =deleted, because the non-static data-member data_ is only copyable, but not movable?
Update. A side-question: For Question q; will std::move(q) still work? Will the fallback to copy happen there? Or will the implicitly-declared move-ctor force the compiler to stop with an error? Here it does compile.
Update 2. What does the compiler generate for the non-movable data-members if I declare the move-ctor Question(Question&&) =default? Does it then fallback to copying those?
You read it incorrectly. This would break lots of C++03 classes in cases such as the following
Question getQuestion();
Question q(getQuestion()); // use of deleted move constructor!
Instead, the FDIS says that a move constructor will be declared iff {there is no user declared {copy constructor, {copy, move} assignment operator, destructor} and the implicitly declared move constructor would not be defined as deleted}.
Regarding Update 2. It has been brought to my attention that if you explicitly-default the move constructor, it will be defined as deleted by the condition
for the move constructor, a non-static data member or direct or virtual base class with a type that does not have a move constructor and is not trivially copyable.
In the following, the move constructor will be defined as deleted, because CopyOnly is not trivially copyable.
struct Question
{
std::vector<int> data_;
CopyOnly copyOnly_;
Question(Question&&) = default;
};