[英]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_constructible
和std::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.