Почему std::lock_guard не может быть перемещен?

Почему std::lock_guard не перемещаемый, это сделало бы код намного лучше:

auto locked = lock_guard(mutex);

вместо

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

Что-то не так с созданием собственной версии, например:

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);
}

?

По какой-то фундаментальной причине было бы плохой идеей сделать его подвижным?

2 ответа

Решение

lock_guard всегда занят; он всегда содержит ссылку на мьютекс и всегда разблокирует его в деструкторе. Если бы он был подвижным, он должен был бы держать указатель вместо ссылки и проверять указатель в его деструкторе. Это может показаться тривиальной ценой, но философия C++ заключается в том, что вы не платите за то, что не используете.

Если вы хотите подвижный (и съемный) замок, вы можете использовать unique_lock,

Возможно, вас заинтересует вывод параметра шаблона n3602 для конструкторов, который устраняет необходимость make_ функции. Это не будет в C++14, но мы можем надеяться на C++17.

Ты можешь сделать:

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

Очевидно, что это не совсем удовлетворительно, так как это не приводит к выводу. Ваша попытка создать фабрику вывода почти готова, за исключением того факта, что вам нужно использовать инициализацию списка для возврата неподвижного объекта:

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

что позволяет auto&& g = lock_guard(mutex);,

(Неловкий танец с std::adopt_lock связано с явным унарным конструктором. Так что мы не можем сделать return { mutex }; поскольку это недопустимое обращение, в то время как return std::lock_guard<Mutex> { mutex }; выполняет инициализацию списка временного объекта, который мы не можем затем переместить в возвращаемое значение.)

Другие вопросы по тегам