"Azumanga" <az******@bubbl escope.net> wrote in message
I am trying to define a series of iterators for a container I am
building, and as forward, bidirectional and random access iterators are
very similar I thought it would be handy to put them into a heirachy,
much like the code below:
struct MyForwardIterat or {
typedef forward_iterato r_tag iterator_catego ry;
// forward iterator functions
};
struct MyBidirIterator : public MyForwardIterat or {
typedef bidirectional_i terator_tag iterator_catego ry;
// extra bidir functions
};
struct MyRandomAccessI terator : public MyBidirIterator {
typedef random_access_i terator_tag iterator_catego ry;
//extra random access functions
};
While this appears to work fine, I feel a little nervous about
overloading typedefs in the different classes. Should I be worried and
is there any unpleasent errors that could arise from this kind of thing?
It seems OK to me to override static functions and types (ie. nested classes
and typedefs).
The problem with overriding non-virtual member functions is that when you
call the function from a pointer to the base class or pointer to the derived
class you get different behavior, which is not intuitive. In reality, the
same can be said for static functions and types.
(Though as an aside I think it's OK to override non-virtual functions if you
return the same object in a covariant manner, so the base class returns a
std::auto_ptr<B ase> and the derived class returns the same object as a
std::auto_ptr<D erived>).
However, because C++ is a statically typed language, the argument does not
hold much sway for static types. Sure, the derived class may point to a
different type. But when you write
template <class Container>
typename Container::valu e_type product(const Container& c) {
if (c.empty()) throw ContainerHasZer oSize("product" );
typename Container::valu e_type result = c.first();
typedef typename Container::cons t_iterator Iter;
Iter iter = c.begin();
const Iter end = c.end();
for (++iter; iter!=end; ++iter) result *= *iter;
return result;
}
the template function 'product' will not be part of the compiled object file
or executable. It is only when you call product(const std::vector<int >&)
that the compiler instantiates the template function 'product' for that
particular type (namely Container=std:: vector<int>), and the specialization
becomes part of the compiled object file.
Now at compile time we know the exact type of Container, so we can just call
the template function product(const Container&), and the compiler will
instantiate the function for us at compile time, using the correct semantics
of Container, Container::valu e_type, Container::cons t_iterator, and so on.
It will use the correct sizeof, substitute the correct operator*, and so
forth.
Had C++ been a dynamically typed language, with template instantiation
occuring at runtime after we instantiated a particular Container, then
having virtual static types (ie. nested classes and typedefs) would make
sense.
Does this make sense?