If the order doesn't matter, I would just use hash table integers for pointers. std::tr1::unordered_map<int, T *> (or std::unordered_map<int, unique_ptr<T>> if C ++ 0x is ok).
Elements of the hash table can move around, so you need to use a pointer, but it will support very fast insert / search / delete. Iteration is also very fast, but the elements will come out in an unspecified order.
Alternatively, I think you can implement your idea as a very simple combination of std::vector and std::list . Just maintain both list<T> my_list and vector<list<T>::iterator> my_vector . To add an object, click on the back of my_list , and then click its iterator on my_vector . (Set the iterator to my_list.end() and scale it down to get an iterator for the last item.) To search, search in the vector and just search for the iterator. To remove, remove from the list (which you can do with an iterator) and set the location in the my_list.end() vector.
std::list ensures that items inside will not move when they are deleted.
[update]
I feel motivated. The first pass during implementation:
#include <vector> #include <list> template <typename T> class NairouList { public: typedef std::list<T> list_t; typedef typename list_t::iterator iterator; typedef std::vector<iterator> vector_t; NairouList() : my_size(0) { } void push_back(const T &elt) { my_list.push_back(elt); iterator i = my_list.end(); --i; my_vector.push_back(i); ++my_size; } T &operator[](typename vector_t::size_type n) { if (my_vector[n] == my_list.end()) throw "Dave not here, man"; return *(my_vector[n]); } void remove(typename vector_t::size_type n) { my_list.erase(my_vector[n]); my_vector[n] = my_list.end(); --my_size; } size_t size() const { return my_size; } iterator begin() { return my_list.begin(); } iterator end() { return my_list.end(); } private: list_t my_list; vector_t my_vector; size_t my_size; };
There is a lack of implementation quality ... How do you probably need additional error checking (what if I delete the same element twice?) And, possibly, some versions of const operator[] , begin() , end() . But this is the beginning.
However, for "several thousand" elements, the map is likely to serve, at least as well. A good rule of thumb is: "Never optimize anything until your profiler tells you."