Extending the C++ Standard Library by Inheritance

Extending the C++ Standard Library by inheritance?

Good nice question. I really wish that the Standard was a little more explicit about what the intended usage is. Maybe there should be a C++ Rationale document that sits alongside the language standard. In any case, here is the approach that I use:

(a) I'm not aware of the existence of any such list. Instead, I use the following list to determine whether a Standard Library type is likely to be designed to be inherited from:

  • If it doesn't have any virtual methods, then you shouldn't be using it as a base. This rules out std::vector and the like.
  • If it does have virtual methods, then it is a candidate for usage as a base class.
  • If there are lots of friend statements floating around, then steer clear since there is probably an encapsulation problem.
  • If it is a template, then look closer before you inherit from it since you can probably customize it with specializations instead.
  • The presence of policy-based mechanism (e.g., std::char_traits) is a pretty good clue that you shouldn't be using it as a base.

Unfortunately I don't know of a nice comprehensive or black and white list. I usually go by gut feel.

(b) I would apply LSP here. If someone calls what() on your exception, then it's observable behavior should match that of std::exception. I don't think that it is really a standards conformance issue as much as a correctness issue. The Standard doesn't require that subclasses are substitutable for base classes. It is really just a "best practice".

Inheriting from c++ standard library List class for adding more methods

I would ask if you really do need to inherit to gain additional functionality. Try using a list as a member of your class. Not only will it make your class easier to change, but it means that code that uses your class won't care about whether its a list or not.

Here is more information

Subclass/inherit standard containers?

Maybe many people here will not like this answer, but it is time for some heresy to be told and yes ... be told also that "the king is naked!"

All the motivation against the derivation are weak. Derivation is not different than composition. It's just a way to "put things together".
Composition puts things together giving them names, inheritance does it without giving explicit names.

If you need a vector that has the same interface and implementation of std::vector plus something more, you can:

  • use composition and rewrite all the embedded object function prototypes implementing function that delegates them (and if they are 10000... yes: be prepared to rewrite all those 10000) or...

  • inherit it and add just what you need (and ... just rewrite constructors, until C++ lawyers will decide to let them be inheritable as well: I still remember 10 year ago zealot discussion about "why ctors cannot call each other" and why it is a "bad bad bad thing" ... until C++11 permitted it and suddenly all those zealots shut up!) and let the new destructor be non-virtual as it was in the original one.

Just like for every class that has some virtual method and some not, you know you cannot pretend to invoke the non-virtual method of derived by addressing the base, the same applies for delete. There is no reason just for delete to pretend any particular special care.

A programmer who knows that whatever is not virtual isn't callable addressing the base, also knows not to use delete on your base after allocating your derived.

All the "avoid this", "don't do that", always sound as "moralization" of something that is natively agnostic. All the features of a language exist to solve some problem. The fact a given way to solve the problem is good or bad depends on the context, not on the feature itself.

If what you're doing needs to serve many containers, inheritance is probably not the way (you have to redo for all). If it is for a specific case ... inheritance is a way to compose. Forget OOP purisms: C++ is not a "pure OOP" language, and containers are not OOP at all.

Proper way to extend the functionality of a container (like std::vector) in C++, without inheriting from it?

First, regarding …

“ I have repeatedly read that inheriting from STL containers is a Bad Thing.”

… with the reference providing the reason that STL containers do not have virtual destructors.

It's certainly good advice for novices to not derive from classes without virtual destructors. That makes them unable to e.g. access the protected member in std::stack, it makes them unable to use Microsoft COM technology, etc., but all in all, for the novice the net advantages are tremendous. Likewise we advice novices to not use raw arrays and direct new and delete, which when followed can be a tremendous net advantage for the novice, but still, some – more experienced – have to do it, have to implement the abstractions that the novices are (ideally) constrained to use, lest there be no dynamic allocation in any C++ program.

So, with classes like std::stack clearly demonstrating that an absolute rule about this is nonsense, the experienced programmer must weight the pros and cons, and when deciding on inheritance, must choose between public, protected and private inheritance.

Con public inheritance: if a novice allocates an auto_vector dynamically, and then attempts to destroy it by using delete on a pointer to std::vector, then the design has failed to guide the novice to correct usage. So if it is a goal to strongly guide novices to correct usage, then either don't do this, or also add functionality that makes dynamic allocation difficult for novices. E.g. add an operator new with additional argument of inaccessible type, or itself inaccessible.

For the case at hand other polymorphic access as std::vector is not a problem, because code that indexes a std::vector beyond its size already has Undefined Behavior.

For other cases, cases that one can imagine, that are different from the OP's case, one must consider the particulars of those other cases: general absolute rules don't cut it in programming (well, at least not in C++ programming).

Pro public inheritance in this case: it's, well, the easiest and most direct and clear expression of "I want all the base class' functionality".

Is it a good idea to extend std::vector?

There's nothing inheritly wrong with this idea, unless you try to delete a vector polymorphically.

For example:

auto myvec = new MyVector<int>;
std::vector<int>* myvecbase = myvec;

delete myvecbase; // bad! UB
delete myvec; // ok, not UB

This is unusual but could still be a source of error.

However, I would still not recommend it.

To gain your added functionalities, you'd have to have an instance of your own vector, which means you either have to copy or move any other existing vectors to your type. It disallows you to use your functions with a reference to a vector.

For example consider this code:

// Code not in your control:
std::vector<int>& get_vec();

// error! std::vector doesn't have reverse!
auto reversed = get_vec().reverse();

// Works if you copy the vector to your class
auto copy_vec = MyVector<int>{get_vec()};
auto reversed_copy = copy_vec.reverse();

Also, it will work with only vector, whereas I can see the utility to have these functionalities with other container types.


My advice would be to make your proposed function free - not make them member of your child class of vector. This will make them work with any instance or references, and also overloadable with other container types. This will make your code more standard ( not using your own set of containers ) and much easier to maintain.

If you feel the need to implement many of those functional style utilities for container types, I suggest you to seek a library that implements them for you, namely ranges-v3, which is on the way to standardisation.


On the other side of the argument, there are valid use case for inheriting STL's class. For example, if you deal with generic code and want to store function object that might be empty, you can inherit from std::tuple (privately) to leverage empty base class optimization.

Also, it happened to me sometime to store a specific amount of elements of the same type, which could vary at compile time. I did extended std::array (privately) to ease the implementation.

However note something about those two cases: I used them to ease the implementation of generic code, and I inherited them privately, which don't expose the inheritance to other classes.

Can I 'extend' a struct in C?

Evidently this feature has been added to C11, but alas I don't have access to a C compiler of recent vintage (>= GCC 4.6.2).

typedef struct foo {
int a;
} foo;

typedef struct bar {
struct foo;
int b;
} bar;

int main() {
bar b;
b.a = 42;
b.b = 99;
return 0;
}

Thou shalt not inherit from std::vector

Actually, there is nothing wrong with public inheritance of std::vector. If you need this, just do that.

I would suggest doing that only if it is really necessary. Only if you can't do what you want with free functions (e.g. should keep some state).

The problem is that MyVector is a new entity. It means a new C++ developer should know what the hell it is before using it. What's the difference between std::vector and MyVector? Which one is better to use here and there? What if I need to move std::vector to MyVector? May I just use swap() or not?

Do not produce new entities just to make something to look better. These entities (especially, such common) aren't going to live in vacuum. They will live in mixed environment with constantly increased entropy.

Is it okay to inherit implementation from STL containers, rather than delegate?

The risk is deallocating through a pointer to the base class (delete, delete[], and potentially other deallocation methods). Since these classes (deque, map, string, etc.) don't have virtual dtors, it's impossible to clean them up properly with only a pointer to those classes:

struct BadExample : vector<int> {};
int main() {
vector<int>* p = new BadExample();
delete p; // this is Undefined Behavior
return 0;
}

That said, if you're willing to make sure you never accidentally do this, there's little major drawback to inheriting them—but in some cases that's a big if. Other drawbacks include clashing with implementation specifics and extensions (some of which may not use reserved identifiers) and dealing with bloated interfaces (string in particular). However, inheritance is intended in some cases, as container adapters like stack have a protected member c (the underlying container they adapt), and it's almost only accessible from a derived class instance.

Instead of either inheritance or composition, consider writing free functions which take either an iterator pair or a container reference, and operate on that. Practically all of <algorithm> is an example of this; and make_heap, pop_heap, and push_heap, in particular, are an example of using free functions instead of a domain-specific container.

So, use the container classes for your data types, and still call the free functions for your domain-specific logic. But you can still achieve some modularity using a typedef, which allows you to both simplify declaring them and provides a single point if part of them needs to change:

typedef std::deque<int, MyAllocator> Example;
// ...
Example c (42);
example_algorithm(c);
example_algorithm2(c.begin() + 5, c.end() - 5);
Example::iterator i; // nested types are especially easier

Notice the value_type and allocator can change without affecting later code using the typedef, and even the container can change from a deque to a vector.



Related Topics



Leave a reply



Submit