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

Edw*_*ard 2 c++ locking move-semantics c++11

我正在尝试创建一个非常简单的,简单的C++类来实现一个线程安全的列表,即在您访问它时自动锁定的列表.不幸的是,编译器不希望允许我创建并返回包含a的结构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 };
        }
};
Run Code Online (Sandbox Code Playgroud)

这失败了

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

我猜这意味着大括号初始化器列表/ C++ 11统一初始化结构不适用于仅移动类型,如std :: unique_lock.所以我尝试为我的struct创建一个显式构造函数,它将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 };
        }
};
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Moh*_*awi 5

问题:

在LockedListAccess构造函数的上下文中,lock_t&& l实际上是一个l值.所以你需要使用它将其强制转换为r值std::move.一般经验法则:始终是std::mover值引用并始终std::forward转发引用.

解:

LockedListAccess(lock_t&& l, std::list<T>& a) 
    : lock(std::move(l))
    , access(a) 
{}
Run Code Online (Sandbox Code Playgroud)

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

这个:

LockedListAccess locked() {
    return LockedListAccess{ std::move(lock_t{mutex}), underlying_list }
}
Run Code Online (Sandbox Code Playgroud)

可以成为这样:

LockedListAccess locked() {
    return LockedListAccess{ lock_t{mutex}, underlying_list }
}
Run Code Online (Sandbox Code Playgroud)

  • 它不会成为`lock_t&`.它仍然是绑定到临时对象的r值引用,但它用作l值.这一开始让我困惑,但如果你知道什么是l值,实际上是有意义的.IE:任何被命名的东西很可能都是l值而你的`lock_t &&`的名字是`l`; (2认同)