Separating copy/move assignment operator - c++

I've read here: What are move semantics?, under secondary title: Special member functions, the reasons why we should unify both copy assignment operator & move assignment operator into a single move\copy assignment operator,
but what if we wish to forbid copying or moving? should in this case I indicate "deleted" on the forbidden constructor and implement the other? (i.e. separating between the two).
If so, what is the proper way to do it?

If you want to create a class that is movable but not copyable, you should implement the move constructor and mark the copy constructor as deleted.
The copy-and-swap pattern still works (more of a move-and-swap, really). Since the parameter can only be move constructed, only move assignment will be available.
class MyClass
{
MyClass(MyClass&& other) {
// Do your move here
}
MyClass(const MyClass& other) = delete;
MyClass& operator=(MyClass other) {
// You can still use copy-and-swap (move-and-swap, really)
// Since MyClass is non-copyable, only move assignment will be available
}
};
Or you could just create a move assignment operator (MyClass& operator=(MyClass&& other)) instead. The default copy assignment operator will be implicitly deleted if you declare a move constructor.

Related

Why does using the move variant of std::vector::push_back invoke the moved item's copy constructor?

Here's some code that won't compile because push_back is trying to call the copy constructor in MoveOnlyClass, which is deleted:
class MoveOnlyClass
{
public:
MoveOnlyClass() {};
MoveOnlyClass& operator=(const MoveOnlyClass& other) = delete;
MoveOnlyClass(const MoveOnlyClass& other) = delete;
};
int main()
{
std::vector<MoveOnlyClass> vec;
vec.push_back(std::move(MoveOnlyClass()));
}
Why does this happen? Surely the only thing the vector should be calling is the move constructor. What would be the correct way to move an object into a vector?
Deleting the copy constructor/copy assignment function also implicitly deletes the move-constructor/move assignment function. If you intend to make an object movable but not copyable, you also need to default the move-constructor.
class MoveOnlyClass
{
public:
MoveOnlyClass() {};
MoveOnlyClass& operator=(const MoveOnlyClass& other) = delete;
MoveOnlyClass(const MoveOnlyClass& other) = delete;
MoveOnlyClass& operator=(MoveOnlyClass&& other) = default;
MoveOnlyClass(MoveOnlyClass&& other) = default;
};
//Will now compile as you expect
int main()
{
std::vector<MoveOnlyClass> vec;
vec.push_back(std::move(MoveOnlyClass()));
}
Also, std::move(T()) is redundant; constructing an object in-place like that will already make it an R-value, and using std::move when you don't need to may prevent some kinds of compiler optimizations (like Copy Ellision).
The answer by #Xirema seals the deal about the problem in the code, and explains why that is the case.
I just want to back it up with the appropriate excerpts from the language spec, to make things official. So from [class.copy.ctor¶8]:
(8) If the definition of a class X does not explicitly declare a move constructor, a non-explicit one will be implicitly declared as defaulted if and only if
(8.1) X does not have a user-declared copy constructor,
For this we should add that declaring as deleted is still declaring. Hence according to this we don't get the implicitly declared move constructor in your case, as we already have a user-declared copy constructor.
Further, under [dcl.fct.def.delete¶3]:
One can make a class uncopyable, i.e., move-only, by using deleted
definitions of the copy constructor and copy assignment operator, and
then providing defaulted definitions of the move constructor and move
assignment operator.

Why object could be "moved" even lacks move constructor and move assignment operator?

http://en.cppreference.com/w/cpp/language/rule_of_three
I have begun with c++11 a couple of months ago
and have watched the rule of five.
So.. I started putting copy constructor/copy assignment operator/move constructor/move assignment operator with default keyword on every class having virtual destructor.
because the rule told me that if you declare explicit destructor then your class doesn't have its implicit move constructor and move assignment operator anymore.
So I think gcc is going to complain to me that below class due to lack of move constructor and move assignment operator.
But It works well! What happend??
class Interface {
public:
virtual ~Interface() = default; // implicit destructor
};
class ImplA : public Interface {
public:
virtual ~ImplA() = default; // implicit destructor
};
ImplA first;
ImplA second(first); // copy constructor, OK. understood it.
ImplA third(std::move(first)); // move constructor, it's OK. Why?
second = first; // copy assignment, OK. understood it.
second = std::move(first); // move assignment, it's also OK. Why?
So I think gcc is going to complain to me that below class due to lack of move constructor and move assignment operator.
Because the operations required could be performed via copy constructor and copy assignment operator. Interface still has copy constructor and copy assignment operator which are implcitly declared. And rvalues are always possible to be bound to const Interface&.
More precisely, even without move constructor and move assignment operator provided, Interface is still MoveConstructible,
A class does not have to implement a move constructor to satisfy this
type requirement: a copy constructor that takes a const T& argument
can bind rvalue expressions.
and MoveAssignable.
The type does not have to implement move assignment operator in order
to satisfy this type requirement: a copy assignment operator that
takes its parameter by value or as a const Type&, will bind to rvalue
argument.
BTW: If you make the move constructor and move assignment operator delete explicitly then both copy and move operation would fail. Using with an rvalue expression the explicitly deleted overload will be selected and then fails. Using with an lvalue expression would fail too because copy constructor and copy assignment operator are implicitly declared as deleted because of the declaration of move constructor or move assignment operator.

Using swap inside assignment move operator

I c++ programming language 13.6.2 std::swap is used to implement move semantics the idea is below:
class deutscheSchweine{
public:
deutscheSchweine(){std::cout<<"DS\n";}
deutscheSchweine& operator=(const deutscheSchweine& other){
deutscheSchweine tmp;
swap(*this, tmp);
return *this;
}
deutscheSchweine(deutscheSchweine&& other){}
deutscheSchweine& operator=(deutscheSchweine&& other){
swap(*this, other);
return *this;
}
};
int main(){
deutscheSchweine ds;
deutscheSchweine ds2;
ds2 = ds;
I above example after calling assignment we can use move semantics to avid copying from temporary, but this example causes recursively calling move assignment. My question is can we use swap in move semantics but in some proper way?
Implementing copy-assignment via swap is a good idea, but you missed some of the details.
You need to call move on each of the individual members at some point. That can be done by calling swap(*this, other); and implementing a specialization of swap, by directly calling swap on each of the individual members, or by letting std::swap call your move assignment operator.
Move assignment should NOT be implemented using swap.
We already have an excellent guide to the "copy-and-swap" idiom, here: What is the copy-and-swap idiom?
Also read Should the Copy-and-Swap Idiom become the Copy-and-Move Idiom in C++11?
In the end, what you want (assuming your member objects are designed correctly) is:
class deutscheSchweine
{
public:
deutscheSchweine(){std::cout<<"DS\n";}
// defaulted move operations (member-wise moves)
deutscheSchweine(deutscheSchweine&& other) = default;
deutscheSchweine& operator=(deutscheSchweine&& other) = default;
// copy construction is defaulted (member-wise copies)
deutscheSchweine(const deutscheSchweine& other) = default;
// copy assignment uses copy-and-move for exception safety
deutscheSchweine& operator=(deutscheSchweine other)
{
return *this = std::move(other);
}
};

Copy constructor, assignment operator, and destructor code duplication

I understand the need for deep copies, and to ensure my code behaves as desired, I am writing a copy ctor, assignment operator, and dtor for my class.
However, it seems to me that in every case, an assignment operator has to do first what the destructor does (deallocate any dynamically allocated memory, to prevent memory leaks), and then what the copy constructor does (make deep copies of all dynamically allocated data and copy those into the instance being constructed).
Is there a case where an assignment operator should conceptually do something other than the following?
class SomeClass{
//member data, copy ctor, dtor
SomeClass& operator=(SomeClass const& rhs){
//what the destructor does
//what the copy constructor does
}
};
The code is practically identical, and seems like a waste of time to rewrite. Other than invoking the destructor directly at the beginning of the assignment operator, I can't think of a way to reuse the copy constructor code I've already written, since I believe doing something like
*this=rhs
would only recursively invoke the assignment operator, since techinically, "this" has already been constructed.
As mentioned in the comments, your concerns about code duplication is addressed by applying the copy-and-swap idiom:
class SomeClass{
//member data, copy ctor, dtor
SomeClass& operator=(SomeClass rhs){
swap(*this, rhs);
return *this;
}
friend void swap(SomeClass& first, SomeClass& second) {
using std::swap;
swap(first.data, second.data);
//... etc. for other data members
}
};
You have to implement the additional swap function, but your copy constructor and destructor code is reused in a natural way. The copy constructor is used when the source of the assignment is passed to the assignment operator, and the destructor is reused when the argument is destructed when the assignment operator returns.

What is the correct implementation of move constructor (and others)?

I have a simple class which contains an std::vector, and I would like to benefit from move semantics (not RVO) when returning the class by value.
I implemented the move constructor, copy constructor and copy assignment operators in the following way:
class A
{
public:
// MOVE-constructor.
A(A&& other) :
data(std::move(other.data))
{
}
// COPY-constructor.
A(const A& other) :
data(other.data)
{
}
// COPY-ASSIGNMENT operator.
A& operator= (const A& other);
{
if(this != &other)
{
data = other.data;
}
return *this;
}
private:
std::vector<int> data;
};
Are the above implementations correct?
And an other question: do I even have to implement any of these members, or are they auto-generated by the compiler? I know that the copy-constructor and the copy-assignment operator are generated by default, but can the compiler auto-generate the move constructor as well? (I compile this code both with MSVC and GCC.)
Thanks in advance for any suggestions. (I know that there already are some similar questions, but not for this exact scenario.)
They're all unnecessary for this class[*], since it would have implicit ones if you didn't declare any of them.
Your constructors are fine. So the following code ostensibly calls the move constructor:
A f() { return A(); }
A a = f(); // move construct (not copy construct) from the return value of f
In fact move-elision might kick in, in which case only the no-args constructor is actually called. I assume you plan to provide some constructors other than copy and move ;-)
Your copy assignment is fine, it differs from the implicit one only in that it has the self-assignment check, which the implicit one would not. I don't think we should have the argument whether a self-assignment check is worth it or not, it's not incorrect.
You haven't defined a move-assignment operator. Given that you defined the others, you should have done, but if you get rid of the rest it's implicit[*]. The move assignment operator (whether user-defined or implicit) is what ensures that the following code will move instead of copying:
A a;
a = f();
[*] On a completed C++11 implementation, of which so far none exist. You can check on a per-compiler basis whether this feature is implemented yet, and probably you'll end up with some horrible #define shenanigans until MSVC does.
For this exact scenario, You don't need to declare any move/copy/assignment functions. Compiler will generate correct defaults.