Как вернуть std :: lock_guard в std :: pair - PullRequest
0 голосов
/ 25 августа 2018

Когда я возвращаю std::lock_guard в std::pair из функции, я получаю ужасные ошибки. Но когда я упаковываю его в класс, у меня нет проблем (компилируется и работает как положено). Я не могу понять почему. Подробности следуют:

Я разработал небольшой шаблонный класс для удобной блокировки и разблокировки общих объектов. Он не особенно инновационный, но C ++ 17 позволяет ему быть очень компактным и удобным для чтения / записи:

template <typename T> class Locked {
public:
    Locked(T& _object, std::mutex& _mutex)
        : object(_object)
        , lock(_mutex)
    {
    }

    T&                          object;
    std::lock_guard<std::mutex> lock;
};

template <typename T> class Lockable {
public:
    Locked<T>       borrow() { return Locked(object, mutex); }
    Locked<const T> borrow() const { return Locked(object, mutex); }

private:
    T                  object;
    mutable std::mutex mutex;
};

Может использоваться как:

int main()
{
    Lockable<std::vector<int>> lv;

    auto [vec, lock] = lv.borrow();

    std::cout << vec.size() << std::endl;
}

Мой вопрос такой. Класс Locked очень тонкий. Я думал, что мог бы использовать std::pair вместо формального класса, вот так:

#include <iostream>
#include <mutex>
#include <utility>
#include <vector>

template <typename T> class Lockable {
public:
    std::pair<T&, std::lock_guard<std::mutex>>       borrow()
    { return std::pair(object, std::lock_guard<std::mutex>(mutex)); }
    std::pair<const T&, std::lock_guard<std::mutex>> borrow() const
    { return std::pair(object, std::lock_guard<std::mutex>(mutex)); }

private:
    T                  object;
    mutable std::mutex mutex;
};

int main()
{
    Lockable<std::vector<int>> lv;

    auto [vec, lock] = lv.borrow();

    std::cout << vec.size() << std::endl;
}

Но это выдает ужасные, трудно поддающиеся анализу ошибки. Я думаю, что это связано с тем, что std::lock_guard не является подвижным, но для меня это выглядит точно так же, как мой рабочий код. Почему эта вторая версия не работает?

Ответы [ 3 ]

0 голосов
/ 25 августа 2018

С некоторым массажем Lockable компилирует:

template <typename T>
class Lockable {
public:
    auto borrow()       { return std::pair<      T&, std::lock_guard<std::mutex>>{object, mutex}; }
    auto borrow() const { return std::pair<const T&, std::lock_guard<std::mutex>>{object, mutex}; }

private:
    T                  object;
    mutable std::mutex mutex;
};

Живой пример .

Идея состоит в том, чтобы явно указать std::lock_guard в качестве аргумента шаблона в std::pair, но указать mutex в качестве соответствующего аргумента конструктора (действительно, вторая версия не работает, потому что std::lock_guard не является подвижным) , В этом случае будет использоваться перегрузка (3) std::pair::pair.

(Кроме того, поскольку это C ++ 17, я бы предложил использовать std::scoped_lock вместо std::lock_guard).

0 голосов
/ 25 августа 2018

Почему эта вторая версия не работает?

Среди многих перегрузок для построения std::pair ваш код не может разрешить влюбой конкретный.Теперь, помимо правильного и простого решения Dev Null, я оставлю это для дальнейшего ознакомления: вы можете forward-construct your std::lock_guard наряду с передачей T& так, как вы хотите, используяpiecewise_construct_t версия конструктора std::pair():

template <typename T> class Lockable {
public:
    auto borrow()
    {
        return std::pair<T&, std::lock_guard<std::mutex>>(
            std::piecewise_construct,
            std::forward_as_tuple(object), std::forward_as_tuple(mutex));
    }

    auto borrow() const
    {
        return std::pair<T&, std::lock_guard<std::mutex>>(
                    std::piecewise_construct,
                    std::forward_as_tuple(object), std::forward_as_tuple(mutex));
    }

private:
    T                  object;
    mutable std::mutex mutex;
};

Примечание: Кроме того, я изменил тип возвращаемого значения из borrow() на auto, так как мыбыть достаточно явным внутри него с точки зрения того, что возвращается.

0 голосов
/ 25 августа 2018

Существенным отличием является то, что в первом случае вы передаете мьютекс и позволяете типу результата создать std::lock_guard<std::mutex> самостоятельно, а во втором случае вы создаете его самостоятельно, а затем позволяете типу результата попробоватьчтобы переместить-построить его.
Последний не может работать!

К счастью, исправление тривиально, просто передайте мьютекс напрямую.

В качестве отступления рассмотрите возможность инвестировать немного больше в auto чтобы уменьшить шум.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...