Move constructor for std::mutex

Solution 1:

Well... mainly because I don't think they should move. Literally.

In some OS-es a mutex might be modeled as a handle (so you could copy them) but IIRC a pthreads mutex is manipulated in-place. If you are going to relocate that, any threadsafety is going fly right out of the window (how would the other threads know that the mutex had just changed it's memory address...)?

Solution 2:

Remember that C++ takes the "don't pay for what you don't use" philosophy to heart. Let's consider for instance an imaginary platform that uses an opaque type to represent a mutex; let's call that type mutex_t. If the interface to operate on that mutex uses mutex_t* as arguments, for instance like void mutex_init(mutex_t* mutex); to 'construct' a mutex, it might very well the case that the address of the mutex is what's used to uniquely identify a mutex. If this is the case, then that means that mutex_t is not copyable:

mutex_t kaboom()
{
    mutex_t mutex;
    mutex_init(&mutex);
    return mutex; // disaster
}

There is no guarantee here when doing mutex_t mutex = kaboom(); that &mutex is the same value as &mutex in the function block.

When the day comes that an implementor wants to write an std::mutex for that platform, if the requirements are that type be movable, then that means the internal mutex_t must be placed in dynamically-allocated memory, with all the associated penalties.

On the other hand, while right now std::mutex is not movable, it is very easy to 'return' one from a function: return an std::unique_ptr<std::mutex> instead. This still pays the costs of dynamic allocation but only in one spot. All the other code that doesn't need to move an std::mutex doesn't have to pay this.

In other words, since moving a mutex isn't a core operation of what a mutex is about, not requiring std::mutex to be movable doesn't remove any functionality (thanks to the non-movable T => movable std::unique_ptr<T> transformation) and will incur minimal overhead costs over using the native type directly.


std::thread could have been similarly specified to not be movable, which would have made the typical lifetime as such: running (associated to a thread of execution), after the call to the valued constructor; and detached/joined (associated with no thread of execution), after a call to join or detach. As I understand it an std::vector<std::thread> would still have been usable since the type would have been EmplaceConstructible.

edit: Incorrect! The type would still need to be movable (when reallocating after all). So to me that's rationale enough: it's typical to put std::thread into containers like std::vector and std::deque, so the functionality is welcome for that type.