Default move constructor with mutex member - c++

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.

Related

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

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 ?

put disabled copy constructor in private

To disable copy constructor and assignment operator, it is clear that we could do either, since c++11:
class A {
public:
A(const A&) = delete;
A& operator=(const A&) = delete;
}
or for c++03:
class A {
private:
A(const A&);
A& operator=(const A&);
}
however, what happens with this:
class A {
private:
A(const A&) = delete;
A& operator=(const A&) = delete;
}
i guess this also leads to the same result. Is there any side effect?
It doesn't matter what access you give a deleted function - it simply doesn't exist(¹), so it is inaccessible whatever the caller.
The error messages may be slightly more confusing. See for example http:://cpp.sh/9hv7y where the first error is about "private" rather than "deleted".
¹ "it doesn't exist" is a simplification. It exists in the sense that it participates in overload resolution, but it is an error if it is the selected function. Thus
struct only_double {
only_double(intmax_t) = delete;
only_double(double arg);
};
only_double zero(0); // Error - deleted constructor called

`noncopyable` with custom destructor

I need a noncopyable class which has a declared destructor, and naive approach doesn't work: see https://ideone.com/mU8aoc. What's the problem with the destructor, why moving doesn't work the same way as without it? And of course, how to fix it?
For reference, the complete code (same as by the ideone link above):
class noncopyable {
public:
noncopyable(noncopyable &&) noexcept;
noncopyable &operator=(noncopyable &&) noexcept;
protected:
noncopyable() = default;
~noncopyable() = default;
noncopyable(const noncopyable &) = delete;
noncopyable &operator=(const noncopyable &) = delete;
};
class C: noncopyable {
public:
// compiles if this line is uncommented
// C(C&& c);
C() {}
// also compiles if this is commented
~C() {}
};
C a() {
return {};
}
C b() {
return a();
}
int main() {
return 0;
}
For your code to work, class C must be moveable. When it has no declared destructor, it gets a compiler-generated implicit move constructor (and move assignment operator). But when it has a declared ("custom" in your parlance) destructor, the move constructor (and move assignment operator) are no longer provided implicitly. This is for your safety: it is assumed that if you need an explicit destructor you will need explicit move functions as well.
Reference: http://en.cppreference.com/w/cpp/language/move_constructor

Public deleted or private default ctor/assignment/copy ctor?

If I want to forbid copy construction/assignment then is:
class foo
{
public:
foo(const foo&) = delete;
foo& operator = (const foo&) = delete;
};
The same as:
class foo
{
private:
foo(const foo&) = default;
foo& operator = (const foo&) = default;
};
Which is the right way and why?
The right way is the first solution : the copy constructor and assignment operators are not defined, so any attempt to use them will not compile.
class foo
{
public:
foo(const foo&) = delete;
foo& operator = (const foo&) = delete;
};
The second is declaring and defining the implicitly generated forms as private :
An object of type foo is allowed to copy itself.
Any friend class or method is also allowed to copy a foo
So copy construction/assignment is still possible.
You could also use boost::noncopyable as a base class, it does exactly that with c++11 (see the source code here)

How to make a C++ class produce non cloneable objects

How can I make a Class non-cloneable like we can do in Java while creating singleton.
Is there any condition we can put on copy constructor so that an exception can be thrown if user tries to make a copy of an object?
I am a novice in C++ so kindly add any info to this or redirect if an answer is already available for the same.
Just declare copy constructor and copy assign operator private
in C++03
class NonCopyable
{
public:
NonCopyable() { }
private:
NonCopyable(const NonCopyable&);
NonCopyable& operator=(const NonCopyable&);
};
Also you can make a class derive from NonCopyable, AnotherType is un-copyable
class AnotherNonCopyable : private NonCopyable
{
public:
AnotherNonCopyable () {}
}
With C++11:
class NonCopyableType
{
public:
NonCopyableType(const NonCopyableType&) = delete;
NonCopyableType& operator=(const NonCopyableType&) = delete;
};
You can delete the copy constructor and assignment operator:
struct Foo
{
Foo(const& Foo) = delete;
Foo& operator=(const Foo&) = delete;
};
If you don't have C++11 support, make them private, and don't implement them:
struct Foo
{
private:
Foo(const& Foo);
Foo& operator=(const Foo&);
};
Note In C++, class and struct are essentially the same.
Declare the copy-semantics as delete:
//C++11 only
MyClass(MyClass const &) = delete;
MyClass& operator=(MyClass const &) = delete;
That makes the class non-copyable!
//pre-C++11 code
private:
MyClass(MyClass const &); //declare only, don't define it
MyClass& operator=(MyClass const &); //declare only, don't define it
This should also work!
Is there any condition we can put on copy constructor so that an
exception can be thrown if user tries to make a copy of an object.
if you make the copy constructor private, The code will not compile when the programmer tries to make another copy. This is probably better than detecting the error with an exception at runtime.
class foo {
private:
operator = ( const foo& f);
};