I need to implement a queue containing unique entries(no duplicates) in C or C++. I am thinking of maintaining a reference of elements already available in queue but that seems
std::queue
is a container adaptor and uses relatively few members of the underlying Container
. You can easily implement a custom container that contains both: an unordered_map
of reference_wrapper
and a deque
. It needs at least members front
and push_back
. Check inside that hash_map
when push_back
of your container is called and reject accordingly (possibly throw). To give the complete example:
#include
#include
#include
#include
#include
#include
namespace std {
// partial specialization for reference_wrapper
// is this really necessary?
template
class hash> {
public:
std::size_t operator()(std::reference_wrapper x) const
{ return std::hash()(x.get()); }
};
}
template
class my_container {
// important: this really needs to be a deque and only front
// insertion/deletion is allowed to not get dangling references
typedef std::deque storage;
typedef std::reference_wrapper c_ref_w;
typedef std::reference_wrapper ref_w;
public:
typedef typename storage::value_type value_type;
typedef typename storage::reference reference;
typedef typename storage::const_reference const_reference;
typedef typename storage::size_type size_type;
// no move semantics
void push_back(const T& t) {
auto it = lookup_.find(std::cref(t));
if(it != end(lookup_)) {
// is already inserted report error
return;
}
store_.push_back(t);
// this is important to not have dangling references
lookup_.insert(store_.back());
}
// trivial functions
bool empty() const { return store_.empty(); }
const T& front() const { return store_.front(); }
T& front() { return store_.front(); }
void pop_front() { lookup_.erase(store_.front()); store_.pop_front(); }
private:
// look-up mechanism
std::unordered_set lookup_;
// underlying storage
storage store_;
};
int main()
{
// reference wrapper for int ends up being silly
// but good for larger objects
std::queue> q;
q.push(2);
q.push(3);
q.push(2);
q.push(4);
while(!q.empty()) {
std::cout << q.front() << std::endl;
q.pop();
}
return 0;
}
EDIT: You will want to make my_container
a proper model of container (maybe also allocators), but this is another full question. Thanks to Christian Rau for pointing out bugs.