我正在尝试创建一个模板化的单生产者/单消费者队列式容器,该容器将支持任何C++类型(引用除外(。
我打算有两个公共方法enqueue
和dequeue
来支持容器上的操作,我对容器的最初实现如下:
template<typename T, size_t N> class ring_queue {
public:
bool enqueue(const T &v)
{
auto pos = end++;
if (start % N == end % N) {
--end;
return false;
}
end %= N;
std::optional<T> value(v);
std::swap(storage[pos], value);
return true;
}
bool dequeue(T &v)
{
size_t pos = start++;
if (pos == end % N) {
--start;
return false;
}
std::optional<T> value;
std::swap(storage[pos], value);
v = std::move(value.value());
start %= N;
return true;
}
private:
std::array<std::optional<T>,N> storage;
size_t start = 0;
size_t end = 0;
};
正如您所看到的,我在这里使用std::optional
作为存储,其思想是存储中唯一包含值的元素是队列中的元素,因此,如果队列为空时被销毁(start == end
(,则不会调用T
析构函数。
当T
是一个简单的可复制类型,如int
,甚至std::string
时,这就可以很好地工作。
例如,当T
是std::unique_ptr
时,事情就会分崩离析。不可复制类型的想法是,我希望容器在包含它们时拥有它们,然后在元素出列时释放它们。
我该怎么做?
您可以使enqueue
采用转发引用,而不是将函数获得的任何参数转发到T
的构造函数。您也可以跳过临时std::optional<T>
+swap
。示例:
#include <utility>
// ...
template<class... Args>
bool enqueue(Args&&... v) {
// ...
storage[pos] = T{std::forward<Args>(v)...};
// ...
}
这样就可以很好地处理不可复制的内容:
ring_queue<std::unique_ptr<int>, 3> rq;
rq.enqueue(std::make_unique<int>(1));