Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why is std::lock_guard not movable?

Why is std::lock_guard not movable, it would make code so much nicer:

auto locked = lock_guard(mutex);

instead of

std::lock_guard<std::mutex> locked(mutex);

Is there something wrong with creating your own version, like:

template <typename T> class lock_guard_
{
  T* Mutex_;
  lock_guard_(const lock_guard_&) = delete;
  lock_guard_& operator=(const lock_guard_&) = delete;
public:
  lock_guard_(T& mutex) : Mutex_(&mutex)
  {
    Mutex_->lock();
  }
  ~lock_guard_()
  {
    if(Mutex_!=nullptr)
      Mutex_->unlock();
  }
  lock_guard_(lock_guard_&& guard)
  {
    Mutex_ = guard.Mutex_;
    guard.Mutex_ = nullptr;
  }
};

template <typename T> lock_guard_<T> lock_guard(T& mutex)
{
  return lock_guard_<T>(mutex);
}

?

Any fundamental reason it would be a bad idea to make it movable?

like image 838
valoh Avatar asked Mar 19 '14 10:03

valoh


People also ask

What is the role of std :: Lock_guard?

std::lock_guard A lock guard is an object that manages a mutex object by keeping it always locked. On construction, the mutex object is locked by the calling thread, and on destruction, the mutex is unlocked.

Are lock guards deprecated?

And that's why lock_guard isn't deprecated. scoped_lock and unique_lock may be a superset of functionality of lock_guard , but that fact is a double-edged sword. Sometimes it is just as important what a type won't do (default construct in this case).


Video Answer


2 Answers

lock_guard is always engaged; it always holds a reference to a mutex and always unlocks it in its destructor. If it was movable then it would need to hold a pointer instead of a reference, and test the pointer in its destructor. This might seem a trivial cost, but it is C++ philosophy that you don't pay for what you don't use.

If you want a movable (and releaseable) lock you can use unique_lock.

You might be interested in n3602 Template parameter deduction for constructors, which removes the need for make_ functions. It won't be in C++14 but we can hope for C++17.

like image 116
ecatmur Avatar answered Oct 04 '22 09:10

ecatmur


You can do:

auto&& g = std::lock_guard<std::mutex> { mutex };

Obviously this isn’t entirely satisfactory as this performs no deduction. Your attempt at a deducing factory is almost there save for the fact that you need to use list-initialization to return a non-movable object:

template<typename Mutex>
std::lock_guard<Mutex> lock_guard(Mutex& mutex)
{
    mutex.lock();
    return { mutex, std::adopt_lock };
}

which allows for auto&& g = lock_guard(mutex);.

(The awkward dance with std::adopt_lock is due to the unary constructor being explicit. So we can’t do return { mutex }; as that’s a disallowed conversion, while return std::lock_guard<Mutex> { mutex }; performs list-initialization of a temporary — which we can’t then move into the return value.)

like image 31
Luc Danton Avatar answered Oct 04 '22 08:10

Luc Danton