无法将 std::unique_lock 移动到结构中

Can't move a std::unique_lock into a struct

本文关键字:移动 结构 lock unique std      更新时间:2023-10-16

我正在尝试创建一个非常简单的C++类来实现线程安全列表,即当您访问它时会自动锁定的列表。不幸的是,编译器不想允许我创建和返回包含unique_lock的结构。这是我最初尝试的:

template<typename T>
struct LockedQueue {
    private:
        std::mutex mutex;
        using lock_t = std::unique_lock<std::mutex>;
        std::list<T> underlying_list;
    public:
        struct LockedListAccess {
            private:
                lock_t lock;
            public:
                std::list<T> &access;
        };
        LockedListAccess locked() {
            return LockedListAccess{ lock_t{mutex}, underlying_list };
        }
};

这失败了

no matching function for call to ‘LockedQueue<tcp::socket>::LockedListAccess::LockedListAccess(<brace-enclosed initializer list>)

我猜这意味着结构的大括号初始值设定项列表/C++11 统一初始化不适用于 std::unique_lock 等仅移动类型。所以我尝试为我的结构创建一个显式构造函数,该构造函数将unique_lock作为右值引用,并将其移动到成员中:

template<typename T>
struct LockedQueue {
    private:
        std::mutex mutex;
        using lock_t = std::unique_lock<std::mutex>;
        std::list<T> underlying_list;
    public:
        struct LockedListAccess {
            private:
                lock_t lock;
            public:
                std::list<T> &access;
                LockedListAccess(lock_t&& l, std::list<T>& a) :
                    lock(l), access(a) {};
        };
        LockedListAccess locked() {
            return LockedListAccess{ std::move(lock_t{mutex}), underlying_list };
        }
};

但是,这也失败了,给了我错误

error: use of deleted function ‘std::unique_lock<_Mutex>::unique_lock(const std::unique_lock<_Mutex>&) [with _Mutex = std::mutex]’

这个编译器错误特别令人困惑,因为它指向包含 lock(l), access(a) 的行,作为我尝试使用 std::unique_lock 已删除的复制构造函数的行。我声明llock_t&&,那么我怎么可能调用复制构造函数呢?

我在互联网上能找到的大多数资源似乎都表明你可以用std::move四处移动unique_locks,尽管似乎没有人解决如何使用std::move构造一个包含unique_lock的对象的问题。我在这里可能做错了什么?

问题:

LockedListAccess 构造函数的上下文中,lock_t&& l 实际上是一个 l 值。因此,您需要使用 std::move 将其转换回 r 值。一般经验法则:始终std::move r 值引用,并始终std::forward转发引用。

溶液:

LockedListAccess(lock_t&& l, std::list<T>& a) 
    : lock(std::move(l))
    , access(a) 
{}

此外,您不需要移动临时锁定对象,因为编译器会自动将临时对象绑定到 r 值引用。

这:

LockedListAccess locked() {
    return LockedListAccess{ std::move(lock_t{mutex}), underlying_list }
}

可以变成这样:

LockedListAccess locked() {
    return LockedListAccess{ lock_t{mutex}, underlying_list }
}