All the above are bad advice and are recommending you break the rule of zero.
A better way is to create a utility class for dealing with the problem of copying mutexes according to the rule you want applied. For example ( and this may not match your requirements ) the following code
https://godbolt.org/z/Y86jscd6K
#include <iostream>
#include <variant>
#include <mutex>
struct mutex_holder {
std::mutex mutex;
mutex_holder():mutex(){}
mutex_holder(mutex_holder const & other):mutex(){}
};
demonstrates a type called mutex_holder which follows the rule that upon copy the copy always gets a new mutex. You can apply this policy then to your mutex in some class you need to copy
struct A {
mutex_holder mHolder;
int x;
int z;
int y;
};
and then when you need to use the lock
void Foo(A & a)
{
std::scoped_lock(a.mHolder.mutex);
a.x = a.z + a.y;
}
And you can see that the class A is copyable and moveable without writing any custom special member function constructors.
int main(){
A a;
A aa = a;
Foo(a);
Foo(aa);
A aaa = std::move(aa);
Foo(aaa);
}
If you need to protect some of the members during copying you can also implement such policy classes to do this.
#include <iostream>
#include <variant>
#include <mutex>
template <typename T>
struct mutex_holder {
std::mutex mutex;
mutex_holder():mutex(){}
mutex_holder(mutex_holder const & other):mutex()
{
std::scoped_lock lock(mutex);
{
data = other.data;
}
}
T data;
};
struct A {
struct Inner {
int x;
int z;
int y;
};
mutex_holder<Inner> mHolder;
};
void Foo(A & a)
{
std::scoped_lock(a.mHolder.mutex);
a.mHolder.data.x = a.mHolder.data.z + a.mHolder.data.y;
}
int main(){
A a;
A aa = a;
Foo(a);
Foo(aa);
}
Again your business logic classes should not have custom SMF. Let the compiler write them for you.