std::lock_guard<std::mutex> 和 if constexpr 块的问题

Way*_*eng 6 c++ templates mutex lock-guard

有一个类模板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_;
};
Run Code Online (Sandbox Code Playgroud)

将会std::lock_guard在 if constexpr 范围的末尾被破坏。(如果不正确,请纠正。)

为了处理这个问题,我可以将下面的任务代码复制resource_到 if constexpr 范围中,或者只使用原始的,std::mutex例如mtx_.lock()& mtx_.unlock()

有什么方法可以处理这个问题std::lock_guard吗?谢谢。

Ala*_*les 5

只需在解锁状态下构造锁,然后再将其锁定:

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_;
}
Run Code Online (Sandbox Code Playgroud)

请注意,您需要std::unique_lock为此使用,因为std::lock_guard无法解锁构建


pad*_*ddy 5

如果您需要经常做这种事情,也许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;
Run Code Online (Sandbox Code Playgroud)

在这里,我们定义了一个不执行任何操作的类模板,其构造方式与std::lock_guard. 然后,我们使用 withstd::conditional来选择 或std::lock_guardFakeLockGuard具体取决于类型检查的结果。

现在您可以按如下方式使用它:

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_;
};
Run Code Online (Sandbox Code Playgroud)

您可以通过在构造函数中设置断点FakeLockGuard或使其输出某些内容来轻松验证其是否有效。

这就是如何让它在编译时工作。但我认为正如您已经提到的,您可以简单地构造 a unique_lock,然后有条件地锁定它。这样做的好处是让任何需要使用你的代码的人都更加清楚。最后,就看你认为最合适的了。