Just wondering, but what does the above statement do? (As I
understand it, it makes the base class function available for
private use in the derived class. But the base class function
was already available for private use. The using declaration
would make sense if it were public, and it would make sense if
the SectorList class also had a push_back function, which would
otherwise hide the base class function, but neither of those
seems to be the case here.)
a) That paper probably referred to public inheritance. Private
inheritance from STL containers does not present any problems.
b) As for public inheritance from STL container, there are two
commonly mentioned problems:
b1) STL containers do not have virtual destructors.
This is not really a problem. Just don't delete pointers to
the derived classes through pointers of the base class (and
why would you have any use for such polymorphic container
pointers in the first place). It's about as problematic in
practice as inheriting from std::iterator.
I disagree. There is nothing you could conceivably do with an
std::iterator itself. You'd never have a reference to
std::iterator as a parameter to a function, for example; in
fact, you'd never have a reference or a pointer to an
std::iterator anywhere in any reasonable code. The same thing
cannot be said of std::vector. And while I can't think of any
case where you'd ever dynamically allocate an std::vector (and
thus, invoke delete on a pointer to std::vector), it's still a
risk I'd prefer avoiding.
Maybe the solution is to forbid dynamic allocation of such
objects in your coding guidelines, so that any delete of an
std::vector would be considered an error, and caught by code
review.
b2) Functions of the type
template < typename T >
std::vector<T> reverse ( std::vector<T> const & v );
will match any object derived from std::vector but usually
have the wrong return type.
This is a more serious objection. However, it is not at all
clear whether inheritance from std::vector is to blame or
whether the function reverse is just ill-designed. Maybe, it
should be:
template < typename Sequence >
Sequence reverse ( Sequence const & v );
possibly enriched by some template magic to make sure it only
matches sequences.
You mean by maybe using begin() and end() in its implementation
(or more likely rbegin() and rend(), in this case---and the
sequence must support at least bi-directional iteration).
More generally, if I'm deriving publicly from std::vector, I'm
saying my class isA std::vector, so there's probably no problem
with the return type above. All you're seeing is the same sort
of slicing that always occurs when you use derived classes by
value.
In practice, the only cases I've seen where it would make sense
at the design level to publicly derive from std::vector is to
provide special, initializing constructors. Once the object has
been constructed, it *is* an std::vector, for all intents and
purposes.
Come to think of it: I have a case---the FieldArray at my
site---where the constructors and assignment operators are
different from std::vector, but the class does use std::vector
in its implementation, and the non-mutable interface is exactly
that of std::vector. The class goes back to long before the
STL, and is widely used in my code, so I'm not going to change
it now. But thinking about it... the restriction to the
non-mutable interface isn't really necessary, and public
derivation from std::vector< std::string > would make perfect
sense. And thinking about it even more: perhaps just having the
client code declare an std::vector<std::string> directly, and
providing free functions to set its value, would also make
sense.
Therefore, it depends by and large on your code base whether
this problem is easy to avoid or not. If you have functions
like reverse whose signatures have been frozen, it can be
cumbersome to derive from container classes.
With public inheritance from standard containers, you have to
be aware of the above caveats. If you are (and you are
confident that the maintenance programmers who will have to
deal with your code are, too), then inheriting publicly from
standard containers can be justified in certain cases. E.g.,
if you are doing linear algebra, you might want to overload
operator+ to do element wise addition. It would be a bad idea
to just dump that overload into global namespace for all
vectors (e.g., it might interfere with the idea of someone
else to have operator+ denote concatenation of sequences). In
that case, a quick
template < typename ArithmeticType >
struct arithmetic_vector : public std::vector< ArithmeticType > {
// some constructors
};
template < typename ArithmeticType >
arithmetic_vector< ArithmeticType >
operator+ ( arithmetic_vector< ArithmeticType > const & lhs,
arithmetic_vector< ArithmeticType > const & rhs ) {
assert( lhs.size() == rhs.size() );
...
}
can serve as a templated typedef that does not create just an
alias but a true independent type (that will convert
transparently to std::vector in cases needed and with the
right constructor, conversion the other way around is also no
problem). However, private inheritance with a complete set of
forwarding methods is considered cleaner by many.
I very much appreciate this example. It's the sort of thing
that wouldn't occur to me, because it's in a domain I'm not
familiar with. Perhaps the key is the idea that an std::vector
(and its derived classes) shouldn't be allocated dynamically.
Ban such dynamic allocation, and you eliminate the most serious
problem: a delete through the pointer to the base class. (And
you can even ban such allocation, by declaring a private
operator new in your derived class.)
A final real issue with public inheritance from std::vector is
the introduction of additional invariants (e.g., the condition
that the sum of all elements be 0). That cannot work since the
underlying base class allows client code to invalidate the
invariant.
That's a different issue. If you need additional invariants,
then you must derive privately or use containment; since you
also have to replace all modifier functions so that you enforce
the invariant, it's probably just as simple to use containment.
But it does raise an interesting question: is there any way to
promote just the "const" interface of a base class, so that the
derived class "isA" base, but only for clients which don't
modify it in any way. (That would correspond to what I want in
my FieldArray as well.)