Reputation: 2836
I am trying to create a templated single producer/single consumer queue-like container that will support any C++ type (other than references).
I intend to have have two public methods, enqueue
and dequeue
, to support operations on the container, and my initial implementation of the container was as follows:
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;
};
As you can see, I'm using an std::optional
here for the storage, the idea being that the only elements in the storage that contain values are those that are in the queue, so that if the queue is destroyed when it is empty (start == end
), there will be no T
destructors being called.
When T
is a simple copy-able type such as int
, or even std::string
this works fine.
When T
is an std::unique_ptr
, for example, then things fall apart. The idea with non-copyable types is that I want the container to own them while they are contained and then released when the element is dequeued.
How do I do this?
Upvotes: 1
Views: 96
Reputation: 117513
You could make enqueue
take a forwarding reference instead to forward whatever arguments the function gets to the constructor of T
. You could skip the temporary std::optional<T>
+ swap
too. Example:
#include <utility>
// ...
template<class... Args>
bool enqueue(Args&&... v) {
// ...
storage[pos] = T{std::forward<Args>(v)...};
// ...
}
This would then work fine with non-copyables:
ring_queue<std::unique_ptr<int>, 3> rq;
rq.enqueue(std::make_unique<int>(1));
Upvotes: 1