繁体   English   中英

如果存储unique_ptr,则不会构建围绕std :: queue的线程安全包装器,但是std :: queue可以工作

[英]Thread safe wrapper around std::queue doesn't build if storing unique_ptr, but std::queue works

我有一个假定的线程安全阻止队列实现,该实现应该是std :: queue的包装,以下是其实现:

template <typename _Tp>
class SharedQueue
{
  public:
    explicit SharedQueue(bool isBlocking = true) : isBlocking_(isBlocking) {}

    virtual ~SharedQueue() {}

    virtual const _Tp& front()
    {
        std::unique_lock<std::mutex> mlock(mtx_);

        // if this is a blocking queue, wait to be notified when when a new object is added
        if (isBlocking_)
        {
            while (queue_.empty())
            {
                cv_.wait(mlock);
            }
        }

        return queue_.front();
    }

    virtual bool empty() const
    {
        std::unique_lock<std::mutex> mlock(mtx_);

        return queue_.empty();
    }

    virtual size_t size() const
    {
        std::unique_lock<std::mutex> mlock(mtx_);

        return queue_.size();
    }

    virtual void push(const _Tp& value)
    {
        std::unique_lock<std::mutex> mlock(mtx_);

        queue_.push(value);

        if (isBlocking_)
        {
            if (queue_.size() == 1)
            {
                cv_.notify_all();
            }
        }
    }

    virtual void push(_Tp&& value)
    {
        {
            std::unique_lock<std::mutex> mlock(mtx_);

            queue_.push(std::move(value));

            if (isBlocking_)
            {
                if (queue_.size() == 1)
                {
                    cv_.notify_all();
                }
            }
        }
    }

    template <typename... _Args>
    void emplace(_Args&&... __args)
    {
        {
            std::unique_lock<std::mutex> mlock(mtx_);

            queue_.emplace(std::forward<_Args>(__args)...);

            if (isBlocking_)
            {
                if (queue_.size() == 1)
                {
                    cv_.notify_all();
                }
            }
        }
    }

    virtual void pop()
    {
        std::unique_lock<std::mutex> mlock(mtx_);

        if (!queue_.empty())
        {
            queue_.pop();
        }
    }

  private:
    bool isBlocking_;

    mutable std::mutex mtx_;

    mutable std::condition_variable cv_;

    std::queue<_Tp> queue_;
};

我希望能够将unique_ptr放置在此队列中,并且我了解从客户端应用程序将其推入队列时,必须在unique_ptr上调用std :: move。 这是我的问题...在我的主程序中,按如下所示创建std :: queue时,我的程序可以正常编译

std::queue<std::unique_ptr<int32_t>> q1;

但是,当我如下创建SharedQueue的实例时,该程序无法编译。

SharedQueue<std::unique_ptr<int32_t>> q2;

我收到一条错误消息,告诉我复制构造函数已在unique_ptr类中删除,这是可以理解的。 我想我只是想知道什么std :: queue可以编译代码,而我的代码“似乎”是std :: queue的包装,并且以类似的方式实现操作。

编辑:如果我替换,我可以让SharedQueue进行编译

queue_.push(value);

queue_.push(std::forward<_Tp>(const_cast<_Tp&>(value)));

在带有左值引用的push方法内部。

您的问题取决于:

    virtual void push(const _Tp& value)
    {
        std::unique_lock<std::mutex> mlock(mtx_);
        queue_.push(value);
     [...]

如果删除了virtual关键字,它将很好地编译,并且会退回到std::queue情况:如果使用该函数,它将仅发出错误。

我假设使用virtual会发出错误,因为由于它要求编译器生成vtable,因此需要生成并指向函数代码。

使其保持虚拟状态并仍可正常工作的一种方法可能是将SFINAE与从您的基础继承的类的std::is_copy_constructiblestd::is_move_constructible专业化一起使用,而push成员函数是纯虚拟的。


这里是一个(糟糕的。不要那样做OO C ++。确实)该想法的示例实现。 请注意,以同样的方式,您不能只是“不调用代码”虚拟push ,也不能只是删除专业化推送,而必须在运行时执行某些操作。 在此示例中,将引发异常。

有更好的方法可以做到这一点。 我正在考虑您真的需要这些成员函数是虚拟的。 否则,只需删除关键字即可解决您的问题。

其中之一是使你SharedQueue类的完全专业化,把push在您需要的过载,但重用的组成方式的代码的其余部分,而不是通过继承。

另一个可能是在queue包装器上做同样的事情,该包装将变得更小并且更具可读性,然后在当前代码中使用它( Example ,再次不好,但是说明了这一点)。

优雅的C ++ 17或更高版本的方法只是使用if constexpr

virtual void push(const _Tp& value) {
    std::unique_lock<std::mutex> mlock(mtx_);

    if constexpr(std::is_copy_constructible_v<_Tp>){
      queue_.push(value);
    } else{
      throw std::invalid_argument("The type _Tp can't be copy constructed");
    }

    if (isBlocking_) {
      if (queue_.size() == 1) {
        cv_.notify_all();
      }
    }
  }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM