I am trying to implement an iterator interface for a templated type hierarchy that uses static polymorphism to avoid overhead associated with solutions that forward to an implementation (unless it can be compiled away).
I've included a short demonstration of the types and interfaces I'm building, and inline comments referencing where I am getting stuck.
The top of the hierarchy shown directly below is a generic Box<T>. The Convert method may return a new Box<U> by applying a function U Func(Box<T>) function to each element.
template<typename T>
class Box : public std::enable_shared_from_this<Box<T>> {
public:
template <typename Func>
auto Convert(Func f) -> std::shared_ptr<Box<decltype(f(std::declval<T>()))>>;
// This is what I am trying to achieve using a statically
// compiled solution (e.g. without the use of a subclass
// returning an implementation pointer and using pimpl).
//
// const_iterator cbegin();
// const_iterator cend();
// Uses the sub-class cbegin/cend interface
std::vector<T> elements() {
std::vector<T> result;
std::copy(cbegin(), cend(), std::back_inserter(result);
return result;
}
};
And here is a ConvertedBox<T> that stores a pointer to a Box and a conversion function. Here the iterator is a wrapper around the referenced variable rdd_ that that applies func_ each time the iterator is dereferenced.
template<typename T, typename U, typename Func>
class ConvertedBox : public Box<T> {
public:
ConvertedBox(typename std::shared_ptr<Box<U>> box, Func func) :
box_(box), func_(func)
{}
// Return iterator wrapper around box_->cbegin()
//const_iterator cbegin()
//const_iterator cend()
private:
std::shared_ptr<Box<U>> box_;
Func func_;
};
And finally we provide a source box that wraps a concrete vector (other sources are used in practice).
template<typename T>
class VectorBox : public Box<T> {
public:
ConvertedBox(const std::vector<T>& data) : data_(data)
{}
// Return iterator wrapper std::vector<T>::const_iterator
//const_iterator cbegin() {
// return data_.cbegin();
//}
//const_iterator cend() { ... }
private:
const typename std::vector<T>& data_;
};
I've tried all sorts of CRTP tricks involving std::iterator and cannot seem to put the pieces together for this. I would like to avoid anything that relies on boost, but assume anything in C++11 in OK.
Note that I am only having problems figuring out this iterator situation (the rest of the parts I've included are just for context).
Related
Let's suppose that I am coding a library, and I want to create a hierarchy of iterators in the same way that they are specified in the standard.
input iterator <- forward iterator <- random access iterator <- bidirectional... <- contiguous...
|| output iterator
I would like to offer a set of "generic iterators", that could be easily used for contiguous containers, like Array, to help the client to reduce the code that needs to write for an iterator.
So, first of all, I replicated std::iterator in my library.
export module iterator:legacy_iterator;
import std;
export namespace zero::iterator::legacy {
template<
iter_concepts::std_iterator_category Category,
typename T,
typename pointer_type = T*,
typename reference_type = T&
> struct iterator {
using value_type = T;
using iterator_category = Category;
using pointer = pointer_type;
using reference = reference_type;
using difference_type = std::ptrdiff;
};
}
the iterator_category concept is just a concept that makes sure that the client is passing one of the iterator category tags defined in the standard.
So, I want to offer a generic implementation of an input iterator, there we go:
export module iterator:legacy_input_iterator;
import :legacy_iterator;
import std;
export namespace zero::iterator::legacy {
template <typename T>
struct input_iter: iterator<std::input_iterator_tag, T> {
using base_it = iterator<std::input_iterator_tag, T>;
private:
typename base_it::pointer _ptr;
public:
input_iter<T>() = default;
explicit input_iter<T>(typename base_it::pointer ptr = nullptr)
: _ptr { ptr } {}
~input_iter<T>() = default;
input_iter<T>(const input_iter<T>& other) = default;
input_iter<T>(input_iter<T>&& other) noexcept = default;
auto operator=(typename base_it::pointer ptr) -> input_iter<T>& { _ptr = ptr; return *this; }
auto operator=(const input_iter<T>&) -> input_iter<T>& = default;
auto operator=(input_iter<T>&&) noexcept -> input_iter<T>& = default;
[[nodiscard]]
auto operator->() const noexcept -> typename base_it::pointer {
return _ptr;
}
[[nodiscard]]
auto operator*() const noexcept -> typename base_it::reference {
return *_ptr;
}
auto operator++() noexcept -> input_iter& {
++this-> _ptr;
return *this;
}
void operator++(int) noexcept {
++(*this);
}
[[nodiscard]]
friend auto operator==(input_iter& self, input_iter& rhs) noexcept -> bool {
return self._ptr == rhs._ptr;
}
[[nodiscard]]
friend auto operator!=(input_iter& self, input_iter& rhs) noexcept -> bool {
return self._ptr != rhs._ptr;
}
};
}
So, now, as a client, I notice that the input iterator in the library, could fit my needs, so I just create my own container, and I try to use it.
template<typename T, size_t N>
class Array {
private:
T array[N];
// code goes on here...
public:
using iterator = zero::iterator::legacy::input_iter<T>;
using const_iterator = zero::iterator::legacy::input_iter<const T>;
};
Everything is fine! I can iterate my array in range for loops, retrieve the begin and end iterators...
Now, going back to the origin, as the library creator, I want to offer at least one generic implementation for every generic category. So, I will try to implement a forward iterator, but I noticed that they must implement the same operators! But with subtle differences...
For an input iterator, after the use of operator++() any copies of the previous value are no longer validity guarantee, but a forward iterator must ensure the oposite
But also, a forward iterator:
Must satisfy legacy input iterator.
Default constructible.
Must be Multipass Guarantee changes to another copy of an iterator like incrementing/assignment (if mutable), can’t change the value read from other copies, unlike Input/Output iterators.
Operators: operator++(int) – returns iterator copy, and operator*() returns a reference to the iterator value.
And now is where I am getting totally lost. In order to follow principles like the DRY principle, or the KISS one, I got tempted to write this:
template <typename T>
struct forward_iterator: public input_iter<T> {
}
So, since the same operations and operators are required, I could use static binding (totally avoiding dynamic dispatch) and get rid out of all the code duplication, and just by changing the private T* ptr member of my input_iterator to protected, everything will look fine. But no...
Is my input iterator even implemented correctly?
Is the container or the datastructure the one who may take care about the iterator specifications? I mean, is the container the responsible for ensuring that the input iterator must be only single pass, and the forward iterator must be multipass, for example? (along with the other requirements)? or should that behaviour must be coded in the iterator?
If the inheritance approach is correct (I think that is a really bad idea), how can I express the specific requirements (if they must be encoded in the operators and operators defined by the iterator)?
How can I then, express the differences between the iterators that has the same operator overloads, without code duplication? (I have a completly different version using CRTP, that solves those problems, but I wanted to know if there's a way with the classical inheritance approach)
Disclaimer: Everything done here is for learning purposes, but as you may see, I am lost without probably basic concepts in C++, like the real implementation of an iterator based on some sort of category defined by the standard, and I just to like to have some nice help guiding me in the architechtural design (and having help with the implementation too would be really kind)
I am trying to make a generic container that would hold objects and their position:
class Vector;
template <typename T>
class Container
{
public:
void insert(const T& t)
{
insertAtPosition(t.getPosition() ,t);
}
private:
void insertAtPosition(const Vector& v, const T& t);
...
} ;
But what if the users' object position getter is not called getPosition?
How can I make this container generic with respect to the way in which the container internally obtains the position of an item?
So far, I have considered 3 approaches, none of them ideal:
Add a std::function<const Vector& (const T& t)> member to the Container.
This is a clean C++ solution, but this function is going to be called very very often and it may result in noticeable performance decrease.
Add a functor object to the container:
class Vector;
template <typename T, typename TGetPosition>
class Container
{
public:
Container(TGetPosition getPosition): getPosition_(getPosition){}
void insert(const T& t)
{
insertAtPosition(getPosition_(t) ,t);
}
private:
void insertAtPosition(const Vector& v, const T& t);
TGetPosition getPosition_;
} ;
I can use the object generator idiom to make it possible to use lambdas:
template <typename T, typename TGetPosition>
Container<T, TGetPosition> makeContainer(TGetPosition getter)
{
return Container<T, TGetPosition>(getter);
}
...
auto container = makeSimpleContainer<Widget>([](const Widget& w)
{
return w.tellMeWhereYourPositionMightBe();
});
There would be no performance overhead, but it would be impossible to get the type of such a container in certain contexts. For example, you could not create a class that would take such a container as a parameter, since decltype would not work, because lambdas cannot be used in unevaluated contexts.
Use #define GETTER getPosition and the user would just change getPosition to whatever he likes. There are so many things wrong with this approach that I don't even know where to start.
Please, is there some other way to do this? Did I miss anything. Any guidance is most welcome!
EDIT:
Regarding solution 2: I have no idea how could we get a the type of a container created with a lambda function. One way would be:
using TContainer = decltype(makeSimpleContainer<Widget>([](const Widget& w)
{
return w.tellMeWhereYourPositionMightBe();
});)
But this doesn't work, because lambdas cannot be used in unevaluated contexts.
Reasonably usable option would be to expect the context to have position_for():
template <class T> struct Container {
size_t insert(T const& x) {
insertAtPosition(position_for(x), x);
}
};
Vector const& position_for(const Widget& w) {
return ...;
}
Container<Widget> c;
c.insert(Widget());
...but designs where the container generates key from the business object usually do not fly well, as to lookup the object one would need to do a dummy one, which may be expensive.
It seems like your second solution will work even if you need to make a class holding a container:
template <typename Container>
struct SomeClass {
SomeClass(const Container &container) : container(container) { }
Container container;
};
int main()
{
auto container = makeSimpleContainer<Widget>([](const Widget& w)
{
return w.tellMeWhereYourPositionMightBe();
});
SomeClass<decltype(container)> test(container);
}
The question is what is the recommended way to use std::list to achieve O(1) erasure of list items?
Usually, when I choose a doubly linked list, I want to be able to remove an element from a list in O(1) time, and then move it to a different list in O(1) time. If the element has its own prev and next pointers, there is no real trick to getting the job done. If the list is a doubly linked circular list, then removal doesn't necessarily require knowing the list that contains the item.
As per Iterator invalidation rules, std::list iterators are very durable. So, it seems to me to get the behavior I desire when using std::list on my own item is to stash an iterator within my class, and the containing list.
class Item {
typedef std::shared_ptr<Item> Ptr;
struct Ref {
std::list<Ptr>::iterator iter_;
std::list<Ptr> *list_;
};
Ref ref_;
//...
};
This has the downside that I will need to create my own decorated version of std::list that knows to update the ref_ whenever the item is added to a list. I can't think of a way that doesn't require the embedded iterator, since not having one would mean erasure would incur a O(n) find operation first.
What is the recommended way to get O(1) erasure with std::list? Or, is there a better approach to achieving the objective?
In the past, I have accomplished this by implementing my own list data structure, where the item placed in the list has its own next and prev pointers. Managing these pointers is natural, since they are inherent to the list operations themselves (the API to my list implementation adjusts the pointers). If I want to use the STL instead, what would be the best way to accomplish this? I offer the straw-man proposal of embedding the iterator. Are there better approaches?
If a concrete use-case is desired, consider a timer implementation. When a timer is created, it is placed into an appropriate list. If it is canceled, it is desirable to efficiently remove it. (This particular example can be solved via marking instead of removal, but it is a valid way to implement cancellation.) Additional use-cases are available upon request.
Another alternative I explored was to fuse a std::list with a std::unordered_map to create a specialized list for pointer types. This is more heavyweight (because of the hash table), but provides a container that is pretty close to the standard containers at the interface level, and gives me O(1) erasure of list elements. The only feature missing from the straw-man proposal is a pointer to the list which currently contains the item. I have put up the current implementation at CodeReview to solicit comment.
std::list::erase is guaranteed to be O(1).
There is not an awful lot of other ways to erase elements from a standard list. (std::list::remove and friends don't do quite the same thing so they don't count).
If you want to erase from a standard list, you need an iterator and the list itself. That's what you seem to already have. There is not very much freedom of doing it differently. I would keep the list containment separate from the objects, unlike what you have done, because why make an object that can be in only one list at a time? Seems like an unnecessary artificial restriction to me. But whatever powers your design.
Maybe you could redesign your interface to hand out iterators instead of raw objects? In the case of your timers example:
class Timer {
// ...
};
typedef std::list<Timer>::iterator TimerRef;
class Timers {
public:
TimerRef createTimer(long time);
void cancelTimer(TimerRef ref);
private:
std::list<Timer> timers;
};
Of course, instead of
timer.cancel();
users of the class now have to say
timers.cancelTimer(timerRef);
but depending on your use case, that might not be a problem.
Update: moving timers between lists:
class Timers {
public:
Timer removeTimer(TimerRef ref);
void addTimer(Timer const &timer);
// ...
};
Usage:
timers2.addTimer(timers1.removeTimer(timerRef));
Admittedly it's a bit cumbersome, but so are the alternatives.
There is no way to have O(1) erasure from std::list.
you may want to consider using an intrusive list, where list nodes are directly imbedded into the structures, like you have already done.
you can use boost::intrusive or roll your own, also check out this
Here's a "complete" solution using an embedded iterator. Some private traits are used to help reduce clutter in the class:
template <typename T> class List;
template <typename T>
class ListTraits {
protected:
typedef std::list<std::shared_ptr<T>> Impl;
typedef typename Impl::iterator Iterator;
typedef typename Impl::const_iterator ConstIterator;
typedef typename Impl::reverse_iterator Rotareti;
typedef typename Impl::const_reverse_iterator ConstRotareti;
typedef std::map<const List<T> *, typename Impl::iterator> Ref;
};
As shown, the list implementation will be using std::list, but the underlying value type will be a std::shared_ptr. What I am after is allowing an instance of T to efficiently derive its own iterator, to achieve O(1) erasure. This is done by using a Ref to memoize the iterator of the item after it is inserted into the list.
template <typename T>
class List : public ListTraits<T> {
template <typename ITER> class IteratorT;
typedef ListTraits<T> Traits;
typename Traits::Impl impl_;
public:
typedef typename Traits::Impl::size_type size_type;
typedef typename Traits::Impl::value_type pointer;
typedef pointer value_type;
typedef IteratorT<typename Traits::Iterator> iterator;
typedef IteratorT<typename Traits::ConstIterator> const_iterator;
typedef IteratorT<typename Traits::Rotareti> reverse_iterator;
typedef IteratorT<typename Traits::ConstRotareti> const_reverse_iterator;
class Item;
~List () { while (!empty()) pop_front(); }
size_type size () const { return impl_.size(); }
bool empty () const { return impl_.empty(); }
iterator begin () { return impl_.begin(); }
iterator end () { return impl_.end(); }
const_iterator begin () const { return impl_.begin(); }
const_iterator end () const { return impl_.end(); }
reverse_iterator rbegin () { return impl_.rbegin(); }
reverse_iterator rend () { return impl_.rend(); }
const_reverse_iterator rbegin () const { return impl_.rbegin(); }
const_reverse_iterator rend () const { return impl_.rend(); }
pointer front () const { return !empty() ? impl_.front() : pointer(); }
pointer back () const { return !empty() ? impl_.back() : pointer(); }
void push_front (const pointer &e);
void pop_front ();
void push_back (const pointer &e);
void pop_back ();
void erase (const pointer &e);
bool contains (const pointer &e) const;
};
This List follows mostly a queue like interface. But, an item may be removed from any position in the list. The simple functions mostly just delegate to the underlying std::list. But the push_*() and pop_*() methods also memoize the iterator.
template <typename T>
template <typename ITER>
class List<T>::IteratorT : public ITER {
friend class List<T>;
ITER iter_;
IteratorT (ITER i) : iter_(i) {}
public:
IteratorT () : iter_() {}
IteratorT & operator ++ () { ++iter_; return *this; }
IteratorT & operator -- () { --iter_; return *this; }
IteratorT operator ++ (int) { return iter_++; }
IteratorT operator -- (int) { return iter_--; }
bool operator == (const IteratorT &x) const { return iter_ == x.iter_; }
bool operator != (const IteratorT &x) const { return iter_ != x.iter_; }
T & operator * () const { return **iter_; }
pointer operator -> () const { return *iter_; }
};
This is the implementation of the helper template used to define the iterator types for List. What it does differently is that the * and -> operators are defined in a way that makes the iterator behave like it is a T * rather than a std::shared_ptr<T> * (which is what the underlying iterator would normally do).
template <typename T>
class List<T>::Item {
friend class List<T>;
mutable typename Traits::Ref ref_;
};
A type T that is derived from List<T>::Item can be added to a List<T>. This base class contains the Ref instance used to memoize the iterator when the item is added to a list.
template <typename T>
inline void List<T>::push_front (const pointer &e) {
const Item &item = *e;
typename Traits::Ref::iterator i = item.ref_.find(this);
if (i == item.ref_.end()) {
item.ref_[this] = impl_.insert(impl_.begin(), e);
} else if (front() != e) {
impl_.erase(i->second);
i->second = impl_.insert(impl_.begin(), e);
}
}
template <typename T>
inline void List<T>::pop_front () {
if (!empty()) {
const Item &item = *front();
item.ref_.erase(this);
impl_.pop_front();
}
}
This code illustrates how the memoization is performed. When doing push_front(), the item is first checked to see if it is already contained. If it is not, it is inserted, and the resulting iterator is added to the ref_ object. Otherwise, if it is not already the front, then the item is removed and reinserted at the front, and the memoized iterator is updated. pop_front() removes the memoized iterator, and then calls pop_front() on the the std::list.
template <typename T>
inline void List<T>::push_back (const pointer &e) {
const Item &item = *e;
typename Traits::Ref::iterator i = item.ref_.find(this);
if (i == item.ref_.end()) {
item.ref_[this] = impl_.insert(impl_.end(), e);
} else if (back() != e) {
impl_.erase(i->second);
i->second = impl_.insert(impl_.end(), e);
}
}
template <typename T>
inline void List<T>::pop_back () {
if (!empty()) {
const Item &item = *back();
item.ref_.erase(this);
impl_.pop_back();
}
}
push_back() and pop_back() are similar to push_front() and pop_front().
template <typename T>
inline void List<T>::erase (const pointer &e) {
const Item &item = *e;
typename Traits::Ref::iterator i = item.ref_.find(this);
if (i != item.ref_.end()) {
item.ref_.erase(i);
impl_.erase(i->second);
}
}
The erase() routine retrieves the memoized iterator, and uses it to perform the erasure.
template <typename T>
inline bool List<T>::contains (const pointer &e) const {
const Item &item = *e;
typename Traits::Ref::iterator i = item.ref_.find(this);
return i != item.ref_.end();
}
Since an item is its own iterator in many respects, a find() method should not be needed in this version of List. But, in lieu of that is this contains() method which is used to see if the element is a member of the list.
Now, the presented solution uses a std::map to associate list instances to iterators. This maintains the spirit of O(1) if the number of lists an item is a member of simultaneously is relatively small.
I will try my hand at a boost::intrusive version next.
Awful truth: while the linked list is powerful structure, the std::list can't fully exploit it's capabilities.
You can't erase the object from std::list using only iterators, because list has to deallocate the node, and you have to know where is the allocator that allocated the memory (hint: it's in a list).
Intrusive containers have many advantages over standard linked list, like self-awareness ;-), ability to store polymorphic objects by value and they make list tricks (like having single objects in multiple lists) feasible. Since you don't use std::list directly anyway, you can drop usage of std::list altogether and use third-party container, or roll your own.
(Also, your solution is also intrusive, since your type have to be derived from List<T>::Item, which places certain requirements on the type that std::list doesn't)
I wrote a short utility function an object to "wrap" an iterable container, so that I could walk it backwards using a range based for.
template <typename Iterable>
struct ReverseWrapper {
private:
Iterable& m_iterable;
public:
ReverseWrapper(Iterable& iterable) : m_iterable(iterable) {}
auto begin() const ->decltype(m_iterable.rbegin()) {
return m_iterable.rbegin();
}
auto end() const ->decltype(m_iterable.rend()) {
return m_iterable.rend();
}
};
template <typename Iterable>
ReverseWrapper<Iterable> reverseIterate(Iterable& list) {
return ReverseWrapper<Iterable>(list);
}
This works for C++ iterable objects, but not for static arrays. What is required for an object to support iteration using a range based for? What would be the best way to approach this problem?
The actual rule to choose begin and end functions for iterables is the following: use the class begin and end function if it has some. Use overloads of the global functions std::begin and std::end if some are provided.
Static arrays not being class/struct, they don't/can't have member functions. The functions called by the foreach loop are the global functions std::begin and std::end, taking an array as parameter. Assuming std::rbegin and std::rend existed, you would have to construct your wrapper the following way:
template <typename Iterable>
struct ReverseWrapper {
private:
Iterable& m_iterable;
public:
ReverseWrapper(Iterable&& iterable) : m_iterable(iterable) {}
auto begin() const -> decltype(rbegin(m_iterable)) {
return rbegin(m_iterable);
}
auto end() const -> decltype(rend(m_iterable)) {
return rend(m_iterable);
}
};
template<typename Iterable>
auto reverseIterate(Iterable&& list)
-> ReverseWrapper<Iterable>
{
return ReverseWrapper<Iterable>(std::forward<Iterable>(list));
}
Even though std::rbegin and std::rend exist in the c++14 standard, they are not available in the c++11 one. So, to get the above code to work with c++11, you would have to implement these functions by hand:
template<typename T, std::size_t N>
auto rbegin(T (&array)[N])
-> std::reverse_iterator<T*>
{
return std::reverse_iterator<T*>(std::end(array));
}
template<typename T, std::size_t N>
auto rend(T (&array)[N])
-> std::reverse_iterator<T*>
{
return std::reverse_iterator<T*>(std::begin(array));
}
In your code, the Iterable template parameter needs to have begin and end member functions. Normal C++ arrays do not have those functions. Instead you have to use std::begin and std::end, which are part of the C++11 standard.
However, there doesn't seem to be any std::rbegin or std::rend functions, which means you have to implement those yourself, possibly also implement the actual iterator class.
I'm implementing four algorithms that are completely identical except for what data structure they use — two use priority_queue, one uses stack, and the last uses queue. They're relatively long, so I'd like to have just one function template that accepts the container type as a template argument and then have each algorithm call that template with the appropriate argument, like so:
template <class Container>
void foo(/* args */)
{
Container dataStructure;
// Algorithm goes here
}
void queueBased(/* args */)
{
foo<queue<Item> >(/* args */);
}
void stackBased(/* args */)
{
foo<stack<Item> >(/* args */);
}
I've managed to do just this with the priority_queue- and stack-based implementations, but I can't do the same for the queue-based algorithm because it uses a different name to access the foremost element (front( ) instead of top( )). I know that I could specialize the template for this case, but then I'd have a big stretch of duplicated code (which is what I'm trying to avoid).
What's the best way to accomplish this? My first instinct was to create a wrapper class for queue that adds a top( ) operation equivalent to stack's, but I've been reading that subclassing STL classes is a no-no. How should I get this behavior, then?
You can write a non-member top function overloaded on the type of the container adapter:
template <typename T>
T& top(std::stack<T>& s) { return s.top(); }
template <typename T>
T& top(std::queue<T>& q) { return q.front(); }
// etc.
If you actually use a different sequence container with the container adapters (via their Sequence template parameter), you'll need to modify the overloads appropriately to handle that.
It might just be more straightforward to use a sequence container (e.g. std::vector) directly rather than using one of the sequence adapters.
You can use partial specialization to select the right method:
template<class Container>
struct foo_detail {
static typename Container::value_type& top(Container &c) { return c.top(); }
static typename Container::value_type const& top(Container const &c) { return c.top(); }
};
template<class T, class Underlying>
struct foo_detail<std::queue<T, Underlying> > {
typedef std::queue<T, Underlying> Container;
static typename Container::value_type& top(Container &c) { return c.front(); }
static typename Container::value_type const& top(Container const &c) { return c.front(); }
};
template<class Container>
void foo(/* args */)
{
Container dataStructure;
// Use foo_detail<Container>::top(dataStructure) instead of dataStructure.top().
// Yes, I know it's ugly. :(
}
You can create a wrapper around std::queue without using inheritance; in fact, inheritance would be the wrong tool here because you're trying to decorate a queue rather than refining or extending the queue. Here's one possible implementation:
template <typename QueueType>
class QueueWrapper {
public:
explicit QueueWrapper(const QueueType& q) : queue(q) {
// Handled in initializer list
}
typedef typename QueueType::value_type value_type;
value_type& top() {
return queue.front();
}
const value_type& top() const {
return queue.front();
}
void pop() {
queue.pop();
}
private:
QueueType queue;
};
Hope this helps!
queue, priority_queue and stack are all container adaptors; they are wrappers around an underlying container (by default, deque for queue and stack and vector for priority_queue).
Since vector, deque and list (the "real" container classes) share most of their methods, you could cut the middle man and use those classes instead.
And keep in mind that public inheritance is not a good idea for STL containers; private inheritance is okay (and probably what you want).
front() and top() are specific to certain types of containers, but all STL containers support *begin().