c++ 如何将不可复制的对象添加到Map中?

i7uaboj4  于 2023-05-30  发布在  其他
关注(0)|答案(3)|浏览(177)

我有以下代码。LockWrap是我想添加到std::map中的内容。
LockWrap继承了一个noncopyable,它完全来自boost库。我不能修改LockWrap的定义。

#include <mutex>
#include <map>

struct base_token {};

class noncopyable: base_token
{
protected:
  constexpr noncopyable() = default;
  ~noncopyable() = default;
  noncopyable( const noncopyable& ) = delete;
  noncopyable& operator=( const noncopyable& ) = delete;
};

class LockWrap : private noncopyable
{
public:
    explicit LockWrap(std::mutex & mut)
        : lock_(mut)
    {}
    std::lock_guard<std::mutex> lock_;
};

LockWrap genLockWrap(std::mutex & mut) {
    return LockWrap(mut);
}

int main() {
    std::mutex mut;
    typedef std::map<int, LockWrap> map_type;
    map_type locks_held;
    int id = 1;
    
    // None of the following can help.
    // locks_held.emplace(std::move(1), genLockWrap(mut));
    // locks_held.insert(map_type::value_type{std::move(1), genLockWrap(mut)});
    // locks_held.insert(std::move(map_type::value_type{std::move(1), genLockWrap(mut)}));
}

我想要的是将LockWrap对象添加到locks_held中。然而,我的尝试都不起作用。std::map<>::insert using non-copyable objects and uniform initialization的方式也不起作用。我想这是因为我没有移动构造函数。
我知道我可以在多个互斥体上创建一个统一的锁来解决我的问题。不过,我还是想知道如何解决这个问题。

avwztpqn

avwztpqn1#

尝试失败的原因是,LockWrap类不仅不可复制,而且由于lock_guard成员而不可移动。因此,使用需要移动构造器将其移动到Map中的creator-function将不起作用。
这样,您既不能复制对象,也不能将对象移动到贴图中,而remaming选项是直接在贴图中就地创建对象。
try_emplace是你的朋友:

locks_held.try_emplace(id, mut);

try_emplace就地创建对象,因此不涉及复制。只需传递构造函数所需的参数即可。

wqnecbli

wqnecbli2#

这是你使用最有用的类型来构造不可移动类型的地方:

// Also known as "lazy"
template<typename F>
struct construct_from {
    F f;
    constexpr operator decltype(f())() const&& {
        return f();
    }
};
template<typename F> construct_from(F) -> construct_from<F>;

然后你只需用途:

locks_held.emplace(std::move(1), construct_from{[&]{ return genLockWrap(mut); });

这将在转换时构造该对的LockWrap成员,因此不需要移动它。

frebpwbc

frebpwbc3#

你基本上有两个选择:

  • 您可以在map中构造LockWrap(通过将互斥体传递给locks_held.emplace(),例如:

godbolt

locks_held.emplace(1, mut);
  • 或者,您可以通过切换到std::unique_lock而不是std::lock_guard,使noncopyableLockWrap类型可移动:

godbolt

class noncopyable: base_token
{
protected:
  constexpr noncopyable() = default;
  ~noncopyable() = default;
  noncopyable( const noncopyable& ) = delete;
  noncopyable& operator=( const noncopyable& ) = delete;
  noncopyable( noncopyable&& ) = default;
  noncopyable& operator=( noncopyable&& ) = default;
};

class LockWrap : private noncopyable
{
public:
    explicit LockWrap(std::mutex & mut)
        : lock_(mut)
    {}
    std::unique_lock<std::mutex> lock_;
};

LockWrap genLockWrap(std::mutex & mut) {
  return LockWrap(mut);
}

// move-construction works now
locks_held.emplace(1, genLockWrap(mut));
locks_held.insert({1, genLockWrap(mut)});
// etc...

注意,当您想要锁定多个互斥锁时,std::scoped_lock可能是更好的解决方案,因为它内置了死锁预防。

std::mutex m1;
std::mutex m2;

std::scoped_lock lock(m1, m2);

相关问题