如何在标准::p空气中返回标准::lock_guard

How to return std::lock_guard in a std::pair

本文关键字:标准 返回 lock guard 空气      更新时间:2023-10-16

当我从函数返回std::pair中的std::lock_guard时,我遇到了可怕的错误。但是当我把它打包在一个类中时,我没有问题(按预期编译和工作(。我不明白为什么。详情如下:

我设计了一个小型模板类,以便方便地锁定和解锁共享对象。它不是特别创新,但 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不可移动有关,但对我来说,它看起来与我的工作代码一模一样。为什么第二个版本不起作用?

通过一些按摩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不可移动(。在这种情况下,将使用std::pair::pair的重载 (3(。

(另外,由于它是C++17,我建议使用std::scoped_lock而不是std::lock_guard(。

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

在构造std::pair的许多重载中,您的代码无法解析为任何特定的重载。现在,除了 Dev Null 正确而直接的解决方案之外,我将其留作进一步参考: 您可以使用std::pair()构造函数的piecewise_construct_t版本,在以您想要的方式传递T&的同时,向前构造您的std::lock_guard

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,因为我们在返回的内容方面足够明确。

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

幸运的是,修复是微不足道的,只需直接传递互斥锁即可。

顺便说一句,考虑在auto上多投资一点以减少噪音。