c++ move semantics : when are they required to be explicit? - c++

Considering for example:
class A
{
public:
A(int v)
{
a_[0]=v;
a_[1]=v;
}
A(A&& other) noexcept
: a_(std::move(other.a_)){}
A& operator=(A&& other) noexcept
{
a_ = std::move(other.a_);
return *this;
}
private:
std::array<int,2> a_;
};
the code corresponding to the move semantics could be considered "obvious" (i.e. moving all the members that are moveable).
If it is omitted, do compilers still manage to generate binaries that will move instances of A in a proper way ?

Related

Default move constructor with mutex member

I have a class with deleted copy constructors and I'm trying to put a mutex member in something like this:
struct A {
A(const A &other) = delete;
A& operator=(const A &other) = delete;
A(A&& other) = default;
A& operator=(A &&other) = default;
std::mutex lock;
};
The compiler is complaining that I'm trying to call the deleted copy constructor, which I summarise to be due to the std::mutex type being non-movable. How can I make the mutex member play with the move constructors with minimal fuss? I don't actually want to move the mutex member itself into the newly constructed object, and would actually like each moved object to just construct it's own mutex
I don't actually want to move the mutex member itself into the newly constructed object, and would actually like each moved object to just construct it's own mutex
Then simply define your move constructor to construct a new mutex:
struct A {
A(const A &other) = delete;
A& operator=(const A &other) = delete;
A(A&& other)
: lock()
{
}
A& operator=(A &&other) = delete;
std::mutex lock;
};
Move assignment will still be a problem and should probably just be deleted. Unless you can answer the question: what happens to the existing mutex member when you're being assigned a new value? Particularly: what if you are assigned a new value while the existing mutex is locked?
As an alternative to providing custom move operations for your class, you could create a generic wrapper:
template <class T>
class PerObject
{
T data;
public:
PerObject() = default;
PerObject(const PerObject&) {}
PerObject& operator= (const PerObject&) { return *this; }
T& operator* () const { return data; }
T* operator-> () const { return &data; }
};
And use it like this:
struct A {
A(const A &other) = delete;
A& operator=(const A &other) = delete;
A(A&& other) = default;
A& operator=(A &&other) = default;
PerObject<std::mutex> lock;
};
The wrapper's copy (and move) operations are no-ops, so the object containing the wrapper will always contain the one it started with.
Caveat: However, based on how your class uses the mutex, the above could actually be dangerous. If the mutex is used to protect other data within the class, then it should likely be locked while the object is being assigned to, so you will have to provide manual move operations anyway. In such case, the code would likely look something like this:
struct A {
A(A&& other) : lock{}, /* anything else you need to move-construct */
{
// Note: it might even be necessary to lock `other.lock` before moving from it, depending on your class's exact semantics and expected use.
}
A& operator=(A &&other)
{
if (this == &other) return *this; // Otherwise, double-locking would be possible
// If you need to lock only this object:
std::unique_lock<std::mutex> l(lock);
// Alternatively, if you need to lock both objects:
std::scoped_lock l(lock, other.lock);
// Now move data from other to this
return *this;
}
std::mutex lock;
};
One way is to make your move constructor to create new mutex when called.
A(A&& other): lock()
{
//... move other things
}
You can also use a std::unique_ptr() to the std::mutex since it is movable.
struct A {
A(const A &other) = delete;
A& operator=(const A &other) = delete;
A(A&& other) = default;
A& operator=(A &&other) = default;
std::unique_ptr<std::mutex> lock;
};
A::A() : lock(new std::mutex())
With this approach you'll not create new mutex each time you move the object which will remove some of the overhead.

why use of deleted rvalue reference constructor function?

This is a very simple example with class A and B.
I want allow only deep copy, so I disabled the rvalue reference constructor.
#include <iostream>
class B;
class A {
public:
A();
~A();
A(const A & other);
A& operator=(const A & other);
A(A && ) = delete;
A& operator=(A && ) = delete;
B toB() const;
private:
int a_;
};
class B {
public:
B();
~B();
B(const B & other);
B& operator=(const B & other);
B(B && ) = delete;
B& operator=(B && ) = delete;
A toA() const;
private:
int b_;
};
A::A()
{
}
A::~A()
{
}
A::A(const A & other)
: a_(other.a_)
{
}
A& A::operator=(const A & other)
{
a_ = other.a_;
return *this;
}
B A::toB() const
{
return B();
}
B::B()
{
}
B::~B()
{
}
B::B(const B & other)
: b_(other.b_)
{
}
B& B::operator=(const B & other)
{
b_ = other.b_;
return *this;
}
A B::toA() const
{
return A();
}
int main()
{
A a();
B b();
return 0;
}
the gcc compiler report bugs like this:
In member function 'B A::toB() const':
error: use of deleted function 'B::B(B&&)'
return B();
^
note: declared here
B(B && ) = delete;
I'm wandering why it use B(B && ) function, not the B(const B &) function instead.
Because you added the move-constructor. Deleted but declared functions are still part of the interface of a class. That means it will be considered by the compiler, but since it's marked as deleted you get the error. If you want to force the copy-constructor to be called then remove the move-constructor declaration.
From this deleted functions reference:
Any use of a deleted function is ill-formed (the program will not compile). This includes calls, both explicit (with a function call operator) and implicit (a call to deleted overloaded operator, special member function, ...
[Emphasis mine]
Since the deleted functions are part of the interface, the compiler will try to use them. So when there is a deleted move constructor, the compiler will see that and try to use it, but since it is deleted there will be an error.
Since no move constructor will be created by the compiler if there is an explicit copy constructor (as per this move constructor reference) simply having a defaulted copy constructor will inhibit moving of object.
All of that means your classes can be very much simplified:
class A {
public:
A() : a_() {}
A(const A & other) = default;
B toB() const;
private:
int a_;
};
class B {
public:
B() : b_() {}
B(const B & other) = default;
A toA() const;
private:
int b_;
};
Because the classes above have user-declared copy constructors no movement constructor will be created, and the class can't be moved only copied.
Move Constructor would be called whenever a nameless object is created.
In B A::toB() const function there is a statement return B(); which creates a nameless object to be returned from the function. For creating a nameless object move constructor is required which is deleted.

copy & swap in base and derived class

I recently read about copy & swap and am now trying to implement the ctors in a base and derived class. I have the four constructors in both my base and derived class, however I am unsure how to implement the assignment operator of the derived class.
explicit Base(int i) : m_i{i} {}
Base(const Base & other) : m_i{other.m_i}
Base(Base && other) : Base(0) { swap(*this, other); }
Base & operator=(Base other) { swap(*this, other); return *this; }
friend void swap(Base & a, Base & b) noexcept {
using std::swap;
swap(a.m_i, b.m_i);
}
explicit Derived(int j) : Base(42), m_j(j) {}
Derived(const Derived & other) : Derived(other.m_j) {}
Derived(Derived && other) : Derived(other.m_j) { swap(*this, other); }
Derived & operator=(Derived other) { /*???*/ }
friend void swap(Derived & a, Derived & b) noexcept {
using std::swap;
swap(a.m_j, b.m_j);
}
Consider using = default as much as possible. And if we are talking about public inheritance, you really need a virtual destructor as well.
Here is how your Base would look using the copy/swap style:
class Base
{
int m_i;
public:
virtual ~Base() = default;
Base(const Base& other) = default;
Base& operator=(Base other) noexcept
{
swap(*this, other);
return *this;
}
Base(Base&& other) noexcept
: Base(0)
{
swap(*this, other);
}
explicit Base(int i) noexcept
: m_i{i}
{}
friend void swap(Base& a, Base& b) noexcept
{
using std::swap;
swap(a.m_i, b.m_i);
}
};
The only difference from what you have is that I've added the virtual destructor, and used = default for the copy constructor.
Now for Derived:
class Derived
: public Base
{
int m_j;
public:
Derived(const Derived& other) = default;
Derived& operator=(Derived other) noexcept
{
swap(*this, other);
return *this;
}
Derived(Derived&& other) noexcept
: Derived(0)
{
swap(*this, other);
}
explicit Derived(int j) noexcept
: Base(42)
, m_j{j}
{}
friend void swap(Derived& a, Derived& b) noexcept
{
using std::swap;
swap(static_cast<Base&>(a), static_cast<Base&>(b));
swap(a.m_j, b.m_j);
}
};
I've let the compiler implicitly take care of the destructor since the compiler will implicitly give me a virtual one that does the right thing in this case.
Again I've explicitly defaulted the copy constructor. This corrects a bug in your version which neglects to copy Base.
The operator= looks just like the Base version.
The Derived move constructor does not need to move or copy anything from other since it is going to swap with other.
The Derived swap function must swap the Base part as well as the Derived part.
Now consider not using the copy/swap idiom. This can be surprisingly easier, and in some cases, higher performing.
For Base you can use = default for all 5 of your special members:
class Base
{
int m_i;
public:
virtual ~Base() = default;
Base(const Base&) = default;
Base& operator=(const Base&) = default;
Base(Base&&) = default;
Base& operator=(Base&&) = default;
explicit Base(int i) noexcept
: m_i{i}
{}
friend void swap(Base& a, Base& b) noexcept
{
using std::swap;
swap(a.m_i, b.m_i);
}
};
The only work that is really required here is your custom constructor and swap function.
Derived is even easier:
class Derived
: public Base
{
int m_j;
public:
explicit Derived(int j) noexcept
: Base(42)
, m_j{j}
{}
friend void swap(Derived& a, Derived& b) noexcept
{
using std::swap;
swap(static_cast<Base&>(a), static_cast<Base&>(b));
swap(a.m_j, b.m_j);
}
};
All 5 of the special members can be implicitly defaulted!
We couldn't default them in the Base because we needed to specify the virtual destructor, which inhibits the generation of the move members, and the generation of the copy members is deprecated with a user-declared destructor. But since we do not need to declare the destructor in Derived, we can just let the compiler handle everything.
As one of the big selling points of copy/swap is reduced coding, it can be ironic that using it can actually require more coding than letting the compiler default the special members.
Of course if the defaults do not do the right thing, then don't use them. I'm simply saying that the defaults should be your first choice, ahead of copy/swap.
You implement op= exactly the same way for Derived as for Base:
Derived& operator=(Derived other) { swap(*this, other); return *this; }
I hope you are aware of the up- and down-sides of passing the argument by value there, though:
Up-side: Only one function needed for all value categories.
Down-Side: Second move for xvalues, move in addition to the needed copy for prvalues.
Other points to consider:
Rule-of-thumb: Single-argument non-copy/move ctors should be explicit: You really don't want to have an implicit conversion from int to Base...
You forgot to re-implement swap for Derived (swap all sub-objects, both base and member). You might forego it if Derived does not add any members though.

moving class (with no default constructor) inside the move constructor of another class

so i have a class with deleted copy ctor/assignment, no default ctor, and has move ctor/assignment:
class A {
int data_ = 0;
public:
A(const A& other) = delete;
A& operator=(const A& other) = delete;
A(int data) : data_(data) {}
~A() {}
A(A&& other) { *this = std::move(other); }
A& operator=(A&& other) {
if (this != &other) {
data_ = other.data_;
other.data_ = 0;
}
return *this;
}
};
and i have a class B (also no default ctor) that contains A:
class B {
A a;
public:
B(const B& other) = delete;
B& operator=(const B& other) = delete;
B(int data) : a(data) {}
~B() {}
B(B&& other) { *this = std::move(other); }
B& operator=(B&& other) {
if (this != &other) {
a = std::move(other.a);
}
return *this;
}
};
now the problem is the B move ctor wont compile, because he says theres no default constructor for A, and this is really annoying, i dont want him to create a new A instance when i call the move ctor on B, i want it to move it!
so theres 2 things i could do:
B(B&& other) : a(std::move(other.a)) { *this = std::move(other); }
this wont work, because in the move assignment he will try to move A again.. also if "this == &other == true" he will have moved A from himself making A garbage now..
another way:
make a default private A ctor. make B friend of A. but that sounds so hacky and ugly..
what is the best way to deal with this situation? i really want to avoid having to make a default constructor for A.
thanks in advance.
The solution would be to do this:
class A {
int data_ = 0;
public:
A(const A& other) = delete;
A& operator=(const A& other) = delete;
A(int data) : data_(data) {}
~A() {}
A(A&& other) : data_(other.data_) { other.data_ = 0; }
A& operator=(A&& other) {
if (this != &other) {
data_ = other.data_;
other.data_ = 0;
}
return *this;
}
};
class B {
A a;
public:
B(const B& other) = delete;
B& operator=(const B& other) = delete;
B(int data) : a(data) {}
~B() {}
B(B&& other) : a(std::move(a)) { }
B& operator=(B&& other) {
if (this != &other) {
a = std::move(other.a);
}
return *this;
}
};
Although, since A only contains an int in it, it would not result in better performance than a copy...
Since you have an Object of A declared in your class B, you need to create it when you construct B. A normal Object in C++ can't have no value and it could not be created (cause there is no default constructor).
To fix your problem, make a pointer to a that can have the value 0, so it needn't to be created in your constructor. Change this line:
A a;
into this:
private:
A *a = 0;

Danger with virtual base move assignment operators when they are now allowed to be used?

This concerns the resolution of C++ Issue http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1402 . Summary:
template<typename T>
struct wrap
{
wrap() = default;
wrap(wrap&&) = default;
wrap(const wrap&) = default;
T t;
};
struct S {
S(){}
S(const S&){}
S(S&&){}
};
typedef wrap<const S> W;
// Error, defaulted move constructor of "wrap<const S>" is deleted!
W get() { return W(); }
(The issue is that we are getting an error for this snippet, even though the compiler could simply use the copy constructor of "S", as it does when the user explicitly writes the move constructor of "wrap". The issue was resolved to ignore deleted move constructors (and assignment operators) during overload resolutions, hence using the copy constructor above as desired.)
When the resolution was drafted, the following comment was made about this resolution:
There are no additional restrictions for implicit/defaulted move operations relative to copy. This means that move assignment in a virtual base is dangerous (and compilers should probably warn) [...] But we decided in Batavia that we weren't going to preserve all C++03 code against implicit move operations, and I think this resolution provides significant performance benefits.
Can someone please describe what the concern with virtual base move assignment operators is?
Consider:
#include <iostream>
struct A
{
A() = default;
A(const A&) = default;
A(A&&) = default;
A& operator=(const A&) {std::cout << "operator=(const A&)\n"; return *this;}
A& operator=(A&&) {std::cout << "operator=(A&&)\n"; return *this;}
};
struct B
: virtual public A
{
};
struct C
: virtual public A
{
};
struct D
: public B,
public C
{
};
int
main()
{
D d1, d2;
d1 = std::move(d2);
}
I believe this program should output:
operator=(A&&)
operator=(A&&)
For me it actually outputs:
operator=(const A&)
operator=(const A&)
But I think this is just a clang bug (compiled with -std=c++1y). If I am correct about what the output should be, then the danger is that the move assignment operator is called twice. This is harmless (though potentially expensive) with the copy assignment operator, but not with the move assignment operator.