Pytanie dlaczego std :: lock_guard nie jest ruchomy?


Dlaczego std :: lock_guard nie jest ruchomy, dzięki temu kod byłby o wiele lepszy:

auto locked = lock_guard(mutex);

zamiast

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

Czy jest coś złego w tworzeniu własnej wersji, na przykład:

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

?

Czy jest jakiś zasadniczy powód, dla którego byłoby to złe?


18
2018-03-19 10:18


pochodzenie


Cóż, masz unique_lock. Możliwe, że interfejs będzie tak prosty, jak to tylko możliwe. - ecatmur
dziękuję, przeoczyłem wyjątkową blokadę :-) Tak więc myślę, że to odpowiada na moje pytanie: Nie, nie ma powodu. Imo czyni go ruchomym, nie czyni interfejsu bardziej skomplikowanym, ale bardziej użytecznym i kompatybilnym z nowoczesnym c ++. - valoh
@valoh: gdyby był ruchomy, musiałby mieć stan, w którym nie ma blokady. To czyni go zbędnym, ponieważ oferuje dokładnie taką samą funkcjonalność, jak unique_lock (chociaż twierdzę, że i tak jest w większości zbędny). Dlatego jeśli lock_guard jest pożądany jako oddzielna klasa, nie może być naprawdę ruchoma. - Grizzly


Odpowiedzi:


lock_guard jest zawsze zaręczony; zawsze zawiera odniesienie do muteksu i zawsze odblokowuje go w swoim destruktorze. Jeśli był ruchomy, musiałby trzymać wskaźnik zamiast referencji i testować wskaźnik w swoim destruktorze. To może wydawać się banalnym kosztem, ale jest to filozofia C ++, której nie płacisz za to, czego nie używasz.

Jeśli chcesz ruchomą (i zwalnianą) blokadę, możesz użyć unique_lock.

Możesz być zainteresowany n3602 Odliczanie parametrów szablonu dla konstruktorów, co eliminuje potrzebę make_ Funkcje. Nie będzie w C ++ 14, ale możemy mieć nadzieję na C ++ 17.


14
2018-03-19 11:21



N3602 to EWG issue 60. EWG zdecydowanie popiera artykuł, ale są pewne kwestie, które wymagają prasowania. - Casey


Możesz to zrobić:

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

Oczywiście nie jest to w pełni zadowalające, ponieważ nie wykonuje żadnych odliczeń. Twoja próba dedukcji fabryki jest prawie taka sama, z wyjątkiem faktu, że musisz użyć inicjalizacji listy, aby zwrócić obiekt niezwiązany z ruchami:

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

co pozwala auto&& g = lock_guard(mutex);.

(Niezręczny taniec z std::adopt_lock jest spowodowane jednoznacznym konstruktorem unarnym. Więc nie możemy tego zrobić return { mutex }; ponieważ jest to niedozwolona konwersja, podczas gdy return std::lock_guard<Mutex> { mutex }; wykonuje inicjalizację listy tymczasowej - której nie możemy potem przenieść do wartości zwracanej.)


10
2018-03-19 11:38



To jest fajne, trochę przerażające, i mogę je ukraść. : p - Konrad Rudolph
To fajne, ale po co dodatkowy poziom agregacji? Wydaje się działać bez niego: ideone.com/KDs8qI - Vaughn Cato
@VaughnCato To mogło być błędne mniemanie, ponieważ język, który według mnie miał zastosowanie tylko do agregatów, wydaje się działać równie dobrze dla nieagregatów. Wprawdzie nie są to moje ulubione obszary Standardu, więc z radością przyjmuję wszelkie światła na nich rzucające. Dzięki za heads-up. - Luc Danton
Sprawdziłem to. Zgodnie z 6.6.3p2, "Instrukcja return z listą wstępnie spreparowanych inicjalizacji inicjuje obiekt lub odniesienie, które mają zostać zwrócone z funkcji przez inicjowanie listy-kopiowania (8.5.4) z określonej listy inicjalizatora.". Mimo że jest to inicjowanie listy kopii, nie oznacza to, że została wykonana kopia. Jedyne, co mogę zobaczyć, sprawia, że ​​inicjowanie listy-kopiowania jest wyjątkowe, ponieważ nie pozwala używać jawnych konstruktorów. W przeciwnym razie jest to regularna inicjalizacja listy i nie ma w niej kopii. - Vaughn Cato