为可复制类型和不可复制类型创建模板容器



我正在尝试创建一个模板化的单生产者/单消费者队列式容器,该容器将支持任何C++类型(引用除外(。

我打算有两个公共方法enqueuedequeue来支持容器上的操作,我对容器的最初实现如下:

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时,这就可以很好地工作。

例如,当Tstd::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));

最新更新