MFC Programmer's SourceBook : Thinking in C++
Bruce Eckel's Thinking in C++, 2nd Ed Contents | Prev | Next

Overview of templates

Now a problem arises. You have an IStack, which holds integers. But you want a stack that holds shapes or airliners or plants or something else. Reinventing your source-code every time doesn’t seem like a very intelligent approach with a language that touts reusability. There must be a better way.

There are three techniques for source-code reuse: the C way, presented here for contrast; the Smalltalk approach, which significantly affected C++; and the C++ approach: templates.

The C approach

Of course you’re trying to get away from the C approach because it’s messy and error prone and completely inelegant. You copy the source code for a Stack and make modifications by hand, introducing new errors in the process. This is certainly not a very productive technique.

The Smalltalk approach

Smalltalk took a simple and straightforward approach: You want to reuse code, so use inheritance. To implement this, each container class holds items of the generic base class object. But, as mentioned before, the library in Smalltalk is of fundamental importance, so fundamental, in fact, that you don’t ever create a class from scratch. Instead, you must always inherit it from an existing class. You find a class as close as possible to the one you want, inherit from it, and make a few changes. Obviously this is a benefit because it minimizes your effort (and explains why you spend a lot of time learning the class library before becoming an effective Smalltalk programmer).

But it also means that all classes in Smalltalk end up being part of a single inheritance tree. You must inherit from a branch of this tree when creating a new class. Most of the tree is already there (it’s the Smalltalk class library), and at the root of the tree is a class called object – the same class that each Smalltalk container holds.

This is a neat trick because it means that every class in the Smalltalk class hierarchy is derived from object, so every class can be held in every container, including that container itself. This type of single-tree hierarchy based on a fundamental generic type (often named object) is referred to as an “object-based hierarchy.” You may have heard this term before and assumed it was some new fundamental concept in OOP, like polymorphism. It just means a class tree with object (or some similar name) at its root and container classes that hold object.

Because the Smalltalk class library had a much longer history and experience behind it than C++, and the original C++ compilers had no container class libraries, it seemed like a good idea to duplicate the Smalltalk library in C++. This was done as an experiment with a very early C++ implementation, [37] and because it represented a significant body of code, many people began using it. In the process of trying to use the container classes, they discovered a problem.

The problem was that in Smalltalk, you could force people to derive everything from a single hierarchy, but in C++ you can’t. You might have your nice object-based hierarchy with its container classes, but then you might buy a set of shape classes or airline classes from another vendor who didn’t use that hierarchy. (For one thing, the hierarchy imposes overhead, which C programmers eschew.) How do you shoehorn a separate class tree into the container class in your object-based hierarchy? Here’s what the problem looks like:

Because C++ supports multiple independent hierarchies, Smalltalk’s object-based hierarchy does not work so well.

The solution seemed obvious. If you can have many inheritance hierarchies, then you should be able to inherit from more than one class: Multiple inheritance will solve the problem. So you do the following:

Now oshape has shape’s characteristics and behaviors, but because it is also derived from object it can be placed in container.

But multiple inheritance wasn’t originally part of C++. Once the container problem was seen, there came a great deal of pressure to add the feature. Other programmers felt (and still feel) multiple inheritance wasn’t a good idea and that it adds unneeded complexity to the language. An oft-repeated statement at that time was, “C++ is not Smalltalk,” which, if you knew enough to translate it, meant “Don’t use object based hierarchies for container classes.” But in the end [38] the pressure persisted, and multiple inheritance was added to the language. Compiler vendors followed suit by including object-based container-class hierarchies, most of which have since been replaced by template versions. You can argue that multiple inheritance is needed for solving general programming problems, but you’ll see in the next chapter that its complexity is best avoided except in certain cases.

The template approach

Although an object-based hierarchy with multiple inheritance is conceptually straightforward, it turns out to be painful to use. In his original book [39] Stroustrup demonstrated what he considered a preferable alternative to the object-based hierarchy. Container classes were created as large preprocessor macros with arguments that could be substituted for your desired type. When you wanted to create a container to hold a particular type, you made a couple of macro calls.

Unfortunately, this approach was confused by all the existing Smalltalk literature, and it was a bit unwieldy. Basically, nobody got it.

In the meantime, Stroustrup and the C++ team at Bell Labs had modified his original macro approach, simplifying it and moving it from the domain of the preprocessor into the compiler itself. This new code-substitution device is called a template[40], and it represents a completely different way to reuse code: Instead of reusing object code, as with inheritance and composition, a template reuses source code. The container no longer holds a generic base class called object, but instead an unspecified parameter. When you use a template, the parameter is substituted by the compiler , much like the old macro approach, but cleaner and easier to use.

Now, instead of worrying about inheritance or composition when you want to use a container class, you take the template version of the container and stamp out a specific version for your particular problem, like this:

The compiler does the work for you, and you end up with exactly the container you need to do your job, rather than an unwieldy inheritance hierarchy. In C++, the template implements the concept of a parameterized type . Another benefit of the template approach is that the novice programmer who may be unfamiliar or uncomfortable with inheritance can still use canned container classes right away.


[37] The OOPS library, by Keith Gorlen while he was at NIH. Generally available from public sources.

[38] We’ll probably never know the full story because control of the language was still within AT&T at the time.

[39] The C++ Programming Language by Bjarne Stroustrup (1st edition, Addison-Wesley, 1986).

[40] The inspiration for templates appears to be ADA generics.

Contents | Prev | Next


Go to CodeGuru.com
Contact: webmaster@codeguru.com
© Copyright 1997-1999 CodeGuru