如何在std :: pair中返回std :: lock_guard

时间:2018-08-25 11:05:44

标签: c++ c++17

当我从函数返回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 :(得分:5)

通过按摩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;
};

Live example

这个想法是在std::lock_guard中明确指定std::pair作为模板参数,但是将mutex作为相应的构造函数参数(实际上,第二个版本不起作用,因为{{ 1}}不可移动)。在这种情况下,将使用std::pair::pair的重载(3)。

(而且,由于它是C ++ 17,我建议使用to use std::scoped_lock instead of std::lock_guard)。

答案 1 :(得分:2)

  

为什么第二个版本不起作用?

many overloads for constructing std::pair中,您的代码无法解析为任何特定的代码。现在,除了在这里Dev Null的正确而直接的解决方案之外,我还要留作进一步的参考:您可以{em> forward-construct 您的 public render() { let stageWithImage = <Stage ref={this.konvaStage} width={300} height={250}> <Layer ref={this.konvaLayer}></Layer> </Stage> return ({ stageWithImage }) ,同时通过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(),因为就返回的内容而言,我们在其中非常明确。

答案 2 :(得分:1)

显着的区别是,在第一种情况下,您传递互斥锁,然后让结果类型自行构造std::lock_guard<std::mutex>,而在第二种情况下,您自己构造它,然后让结果-尝试尝试移动构建它。
后者不起作用!

幸运的是,修复很简单,只需直接传递互斥锁即可。

顺便说一句,请考虑对auto进行更多投资以减少噪音。