有一个类模板
Foo<T>
。对于某些特定类型,函数应该使用lock_guard
。
这是示例代码:
#include <type_traits>
#include <mutex>
#include <vector>
template<typename T>
class Foo {
public:
void do_something(int k) {
if constexpr(std::is_same_v<T, NeedMutexType>) {
std::lock_guard<std::mutex> lock(mtx_);
}
resource_.push_back(k);
// code for task with resource_ ...
}
private:
std::mutex mtx_;
std::vector<int> resource_;
};
std::lock_guard
将在 if constexpr 范围的末尾被破坏。 (如有不妥,请指正。)
为了处理这个问题,我可以将下面带有
resource_
的任务代码复制到 if constexpr 范围内,或者只使用原始的 std::mutex
,例如 mtx_.lock()
& mtx_.unlock()
代替。
有什么方法可以用
std::lock_guard
处理这个问题吗?谢谢。
也许 std::conditional 可以来拯救这里,如果你需要经常做这种事情。
template<class Mutex>
struct FakeLockGuard { FakeLockGuard(Mutex&){} };
template<typename T, class Mutex = std::mutex>
using OptionalLock = typename std::conditional<
std::is_same_v<T, NeedMutexType>,
std::lock_guard<Mutex>,
FakeLockGuard<Mutex>>::type;
这里我们定义了一个什么都不做的类模板,其构造方式与
std::lock_guard
相同。然后,我们将其与 std::conditional
结合使用,根据类型检查的结果选择 std::lock_guard
或 FakeLockGuard
。
现在您可以按如下方式使用它:
template<typename T>
class Foo {
public:
void do_something(int k)
{
OptionalLock<T> lock(mtx_);
resource_.push_back(k);
// ...
}
private:
std::mutex mtx_;
std::vector<int> resource_;
};
您可以通过在
FakeLockGuard
构造函数中设置断点或使其输出一些内容来轻松验证它是否有效。
这就是你如何让它在编译时工作。但是我认为正如您已经提到的那样,您可以简单地构造一个
unique_lock
然后有条件地锁定它。这样做的好处是对于必须使用您的代码的任何人来说都更加清晰。最后,就看你觉得哪个最合适了。
只需在解锁状态下构建锁,然后再锁定:
template<typename T>
class Foo {
public:
void do_something(int k) {
std::unique_lock<std::mutex> lock(mutex_, std::defer_lock);
if constexpr(std::is_same_v<T, NeedMutexType>) {
lock.lock();
}
resource_.push_back(k);
// code for task with resource_ ...
}
private:
std::mutex mtx_;
std::vector<int> resource_;
}
请注意,您需要为此使用
std::unique_lock
,因为std::lock_guard
无法解锁