Chapter 5

Creating Classes with Templates

IN THIS CHAPTER

check Creating class templates

check Using parameters in templates

check Deriving with templates

check Creating function templates

If C++ programming has any big secret, it would have to be templates, which are entities that define either a family of functions or a family of classes. Templates seem to be the topic that beginning programmers strive to understand because they’ve heard about them and seem to think that templates are the big wall over which they must climb to ultimately become The C++ Guru. This chapter begins by showing you that creating and using basic templates need not be difficult.

The one thing you can be certain of is that knowing how to work with templates will open your abilities to a whole new world, primarily because the entire Standard C++ Library is built around templates. Further, understanding templates can help you understand all that cryptic code that you see other people posting on the Internet. This chapter also helps you understand how to access, use, and extend standard templates.

Remember You don’t have to type the source code for this chapter manually. In fact, using the downloadable source is a lot easier. You can find the source for this chapter in the \CPP_AIO4\BookV\Chapter05 folder of the downloadable source. See the Introduction for details on how to find these source files.

Templatizing a Class

This section begins by showing you just how simple templates are to understand. It begins with a discussion of type, which you can skip if you already understand the concept fully. The next section deals with the need for creating templates based on a type.

Considering types

This section begins with the OldGasStation class. Remember, a class is a type. You can declare variables of the type. Thus you can declare a variable of type OldGasStation called, for example HanksGetGas. You can also create another variable of type OldGasStation; maybe this one would be called FillerUp. And, of course, you can create a third one; this one might be called GotGasWeCanFillIt. Each of these variables, HanksGetGas, FillerUp, and GotGasWeCanFillIt, are each instances of the type (or class) OldGasStation.

In the same way, you can make some instances of an existing type, say int. You can name one CheckingAccountBalance and another BuriedTreasuresFound. Each of these is an instance of the type int Although int isn’t a class, it is a type.

Think about this so far: You have the two different types available to you. One is called OldGasStation and the other is called int. One of these is a type you make; the other is built into C++.

Focus on the one you create, OldGasStation. This is a type that you create by declaring it in your application when you write the code. The compiler takes your declaration and builds some data inside the resulting application that represents this type. After the application starts, the type is created, and it doesn’t change throughout the course of the application.

Remember The variables in your application may change at runtime; you can create new instances of a type and delete them and change their contents. But the type itself is created at compile time and doesn’t change at runtime. Remember this as one property of types in general. You need to keep this in mind when dealing with templates.

Defining the need for templates

Suppose that you have a class called MyHolder. This class will hold some integers. Nothing special, but it looks like this:

class MyHolder {
public:
int first;
int second;
int third;
int sum() {
return first + second + third;
}
};

This class is easy to use; you just create an instance of it and set the values of its members. But remember: After the application is running, the class is a done deal. But at runtime, you’re free to create new instances of this class. For example, the following code creates ten instances of the class, calls sum(), and prints the return value of sum():

MyHolder *hold;
int loop;
for (loop = 0; loop < 10; loop++) {
hold = new MyHolder;
hold->first = loop * 100;
hold->second = loop * 110;
hold->third = loop * 120;
cout << hold->sum() << endl;
delete hold;
}

This code creates an instance at runtime, does some work with it, and then deletes the instance. It then repeats this process for a total of ten times. Instances (or variables) are created, changed, and deleted — all at runtime. But the class is created at compile time.

Suppose you’re coding away and you discover that this class MyHolder is handy, except it would be nice if you had a version of it that holds floats instead of ints. You could create a second class just like the first that uses the word float instead of int, like this:

class AnotherHolder {
public:
float first;
float second;
float third;
float sum() {
return first + second + third;
}
};

This class works the same way as the previous class, but it stores three float types instead of int types. But you can see, if you have a really big class, that this method would essentially require a lot of copying and pasting followed by some search-and-replacing — in other words, busywork. But you can minimize this busywork by using templates. Instead of typing two different versions of the class, type one version of the class that you can, effectively, modify when you need different versions of the class. Look at this code:

template <typename T>
class CoolHolder {
public:
T first;
T second;
T third;
T sum() {
return first + second + third;
}
};

Think of this templated class as a rule for a single class that does exactly what the previous two classes did. (Ignore the template declaration, template <typename T>, for now; it’s explained in the “Understanding the template keyword” section of the chapter.) In this rule is a placeholder called T that is a placeholder for a type. Imagine this set of code; then remove the first line and replace all the remaining T’s with the word int. If you did that, you would end up with this:

class CoolHolder {
public:
int first;
int second;
int third;
int sum() {
return first + second + third;
}
};

This is, of course, the same as the earlier class called MyHolder, just with a different name. Now imagine doing the same thing but replacing each T with the word float. You can probably see where we’re going with this. Here it is:

class CoolHolder {
public:
float first;
float second;
float third;
float sum() {
return first + second + third;
}
};

Again, this is the same as the earlier class called AnotherHolder, but with a different name. That’s what a template does: It specifies a placeholder for a class. But it doesn’t actually create a class … yet. You have to do one more thing to tell the compiler to use this template to create a class. You accomplish this task by writing code to create a variable or by using the class somehow. Look at this code:

CoolHolder<int> IntHolder;
IntHolder.first = 10;
IntHolder.second = 20;
IntHolder.third = 30;

This code tells the compiler to create a class by replacing every instance of T with int in the CoolHolder template. In other words, the compiler creates a class named CoolHolder<int>. These four lines of code create an instance of CoolHolder<int> called IntHolder and set its properties. The computer creates this class at compile time. Remember, types are created at compile time, and this example code is no exception to this rule.

Tip Here’s an easy way to look at a template. When you see a line like CoolHolder<int> IntHolder; you can think of it as being like CoolHolderint IntHolder. Although that’s not really what the template is called, you are telling the compiler to create a new class. In your mind, you may think of the class as being called CoolHolderint, that is, a name without the angle brackets. (But remember that the name really isn’t CoolHolderint. It’s CoolHolder<int>.)

Creating and using a template

The previous section tells how to put a template together based on your requirements, so now it’s time to put that template into action. The CoolHolder example, shown in Listing 5-1, contains a complete application that uses the CoolHolder template.

LISTING 5-1: Using Templates to Create Several Versions of a Class

#include <iostream>

using namespace std;

template <typename T>
class CoolHolder {
public:
T first;
T second;
T third;
T sum() {
return first + second + third;
}
};

int main() {
CoolHolder<int> IntHolder;
IntHolder.first = 10;
IntHolder.second = 20;
IntHolder.third = 30;

CoolHolder<int> AnotherIntHolder;
AnotherIntHolder.first = 100;
AnotherIntHolder.second = 200;
AnotherIntHolder.third = 300;

CoolHolder<float> FloatHolder;
FloatHolder.first = 3.1415;
FloatHolder.second = 4.1415;
FloatHolder.third = 5.1415;

cout << IntHolder.first << endl;
cout << AnotherIntHolder.first << endl;
cout << FloatHolder.first << endl;

CoolHolder<int> *hold;
for (int loop = 0; loop < 10; loop++) {
hold = new CoolHolder<int>;
hold->first = loop * 100;
hold->second = loop * 110;
hold->third = loop * 120;
cout << hold->sum() << endl;
delete hold;
}
return 0;
}

When you run this application, you see a bunch of results from calls to sum():

10
100
3.1415
0
330
660
990
1320
1650
1980
2310
2640
2970

Look closely at the code. Near the beginning is the same template shown previously. Remember that the compiler doesn’t create a type for this template. Instead, the compiler uses it as a rule to follow to create additional types. That is, the code indeed serves as a template for other types, thus its name.

Understanding the template keyword

It’s time to consider the template keyword in the template definition. Here’s the first line of the template shown in Listing 5-1:

template <typename T>

All this means is that a template class follows and it has a type with a placeholder called T. Inside the class, anywhere a T appears, the compiler replaces it with the typename defined by T, such as int or float.

Remember The T is stand-alone; if you have it as part of a word, it won’t be replaced. The standard practice is for people to use T for the placeholder, but you can use any identifier (starting with a letter or underscore, followed by any combination of letters, numbers, or underscores). In some cases, templates have more than one replaceable placeholder, each of which is unique.

To use the template, you declare several variables of types based on this template. Here’s one such line:

CoolHolder<int> IntHolder;

This line declares a variable called IntHolder. For this variable, the compiler creates a type called CoolHolder<int>, which is a type based on the CoolHolder template, where T is replaced by int. Here’s another line where the code declares a variable:

CoolHolder<int> AnotherIntHolder;

This time, the compiler doesn’t have to create another type because it just created the CoolHolder<int> type earlier. But again, this line uses the same type based on the template, where T is replaced by int.

The example in Listing 5-1 creates another class based on the CoolHolder template. It’s instantiated at FloatHolder:

CoolHolder<float> FloatHolder;

When the compiler sees this line, it creates another type by using the template, and it replaces T with the word float. So in this case, the first, second, and third properties of FloatHolder each hold a floating-point number. Also, the sum() method returns a floating-point number.

The following line uses the CoolHolder<int> type created earlier to declare a pointer to CoolHolder<int>, hold. Yes, you can do that; pointers are allowed:

CoolHolder<int> *hold;

Then the code that follows cycles through a loop to create new instances of type CoolHolder<int> by using the line

hold = new CoolHolder<int>;

The code accesses the members using the pointer notation, ->, like so:

hold->first = loop * 100;

Remember These are the basics of templates. They’re really not as bad as people make them out to be. Just remember that when you see an identifier followed by angle brackets containing a type or class, it’s a template.

Going Beyond the Basics

The previous section discusses template basics. However, templates are more flexible and powerful than you might imagine. The following sections discuss how you can move beyond the basics to add flexibility to your code.

Separating a template from the function code

In the earlier days of templates and C++, the rule was that you had to put the method code for a class template inside the template itself; you couldn’t put a forward declaration in the template and then put the function code outside the template, as you could do with classes. However, the ANSI standard changed this situation and made putting the code outside the template legal. (It’s important to know this fact because you may encounter convoluted-looking code that puts everything inside.) The ImFree example, shown in Listing 5-2, shows you how to separate the methods from the template.

LISTING 5-2: Separating a Template from Function Code

#include <iostream>

using namespace std;

template <typename T>
class ImFree {
protected:
T x;
public:
T& getx();
void setx(T);
};

template <typename T>
T &ImFree<T>::getx() {
return x;
}

template <typename T>
void ImFree<T>::setx(T newx) {
x = newx;
}

int main() {
ImFree<int> separate;
separate.setx(10);
cout << separate.getx() << endl;
return 0;
}

Look closely at one of the methods:

template <typename T>
T &ImFree<T>::getx() {
return x;
}

The first line is the same as the first line of the template definition. It’s just the word template followed by the parameter in angle brackets.

The next line looks almost like you might expect it to. With classes, you put the function prototype, adding the class name and two colons before the function name itself, but after the return type. Here you do that, too; the sticky part is how you write the template name. You don’t just give the name; instead, you follow the name with two angle brackets, with the parameter inside, like this: T &ImFree<T>::getx(). Note the <T> part.

Tip Note that the getx() method returns a reference instead of a variable of type T. There’s a good reason for doing it. In the main() function of Listing 5-2, you create the class based on the template with an integer parameter:

ImFree<int> separate;

However, you can create the class with some other class:

ImFree<SomeOtherClass> separate;

When you do that, you don’t really want to return just an instance from the function, as in

T& getx() {
return x;
}

Returning an instance copies the existing instance rather than return the existing instance. Using a reference means that the class user doesn’t have to do any bizarre coding. For example, displaying the output using a cout is rather straightforward:

cout << separate.getx() << endl;

Including static members in a template

You can include static members in a template, but you need to be careful when you do so. Remember that all instances of a class share a single static member of the class. You can think of the static member as being a member of the class itself, whereas the nonstatic members are members of the instances.

Now, from a single template, you can potentially create multiple classes. This means that to maintain the notion of static members, you need to either get creative with your rules or make life easy by just assuming that each class based on the template gets its own static members. The easy way is exactly how this process works.

Remember When you include a static member in a template, each class that you create based on the template gets its own static member. Further, you need to tell the compiler how to store the static member just as you do with static members of classes that aren’t created from templates. The StaticMembers example, shown in Listing 5-3, contains an example of static members in a template.

LISTING 5-3: Using Static Members in a Template

#include <iostream>

using namespace std;

template <typename T>
class Electricity {
public:
static T charge;
};

template <typename T>
T Electricity<T>::charge;

int main() {
Electricity<int>::charge = 10;
Electricity<float>::charge = 98.6;
Electricity<int> inst;
inst.charge = 22;

cout << Electricity<int>::charge << endl;
cout << Electricity<float>::charge << endl;
cout << inst.charge << endl;

return 0;
}

Note how you declare storage for the static member; it’s the two lines in between the template and main(). You supply the same template header you would for the class and then specify the static member type (in this case, T, which is the template parameter). Next, you refer to the static member by using the usual classname::member name syntax. But remember that the class name gets the template parameter in angle brackets after it.

This code creates two classes based on the templates Electricity <int> and Electricity <float>. Each of these classes has its own instance of the static member; the <int> version contains 10 and the <float> version contains 98.6. Then, just to show that there’s only a single static member per class, the code creates an instance of Electricity<int> and sets its static member to 22. Using a cout statement, you can see that the output for the two Electricity<int> lines are the same and the Electricity<float> output is different.

Parameterizing a Template

A template consists of a template name followed by one or more parameters inside angle brackets. Then comes the class definition. When you create a new class based on this template, the compiler obliges by making a substitution for whatever you supply as the parameter. Focus your eyes on this template:

template <typename T>
class SomethingForEveryone {
public:
T member;
};

Not much to it: It’s just a simple template with one member called, conveniently enough, member. However, notice in particular what’s inside the angle brackets. This is the parameter: typename T. As with parameters in a function, the first is the type of the parameter (typename), and the second is the name of the parameter (T). Previous sections have illustrated how this all works. However, you don’t always use typename; you can use other types, as described in the sections that follow.

Putting different types in the parameter

It turns out there’s more to using parameters than meets the computer screen. You can put many more keywords inside the parameter beyond just the boring word typename. For example, suppose you have a class that does some comparisons to make sure that a product isn’t too expensive for a person’s budget. Each person would have several instances of this class, one for each product. This class would have a constant in it that represents the maximum price the person is willing to spend.

But there’s a twist: Although you would have multiple instances of this class, one for each product the person wants to buy, the maximum price would be different for each person. You can create such a situation with or without templates. Here’s a way you can do it with a template:

template <int MaxPrice>
class PriceController {
public:
int Price;
void TestPrice()
{
if (Price > MaxPrice)
{
cout << "Too expensive" << endl;
}
}
};

In this case, the template parameter isn’t a type at all — it’s an integer value, an actual number. Then, inside the class, you use that number as a constant. As you can see in the TestPrice function, the code compares Price to the MaxPrice constant. So this time, instead of using T for the name of the template parameter, the code views it as a value, not a type. The PriceController example, shown in Listing 5-4, contains a complete example that uses this template.

LISTING 5-4: Using Different Types for a Template Parameter

#include <iostream>

using namespace std;

template <typename T>
class SomethingForEveryone {
public:
T member;
};

template <int MaxPrice>
class PriceController {
public:
int Price;
void TestPrice(string Name)
{
if (Price > MaxPrice)
{
cout << Name << " too expensive!" << endl;
}
}
};

int main() {
SomethingForEveryone<int> JustForMe;
JustForMe.member = 2;
cout << JustForMe.member << endl;

const int FredMaxPrice = 30;
PriceController<FredMaxPrice> FredsToaster;
FredsToaster.Price = 15;
FredsToaster.TestPrice("Toaster");
PriceController<FredMaxPrice> FredsDrawingSet;
FredsDrawingSet.Price = 45;
FredsDrawingSet.TestPrice("Drawing set");

const int JulieMaxPrice = 60;
PriceController<JulieMaxPrice> JuliesCar;
JuliesCar.Price = 80;
JuliesCar.TestPrice("Car");
return 0;
}

Each person gets a different class that reflects the maximum price they’re willing to pay. You can see that Fred gets a class called PriceController <FredMaxPrice>. Julie, however, gets a class called PriceController <JulieMaxPrice>. And remember, these really are different classes. The compiler created two different classes, one for each item passed in as a template parameter. Also notice that the parameters are constant integer values. FredMaxPrice is a constant integer holding 30. JulieMaxPrice is a constant integer holding 60.

For the first one, PriceController <FredMaxPrice>, the code creates two instances. For the second one, PriceController <JulieMaxPrice>, the code creates one instance. In all instances, the code sets the price of the item and then calls TestPrice() with the item name. If the item is too expensive, the PriceController outputs a special message. Here’s the output from this example:

2
Drawing set too expensive!
Car too expensive!

Warning When working with some older versions of C++, you can’t use certain types, such as float, for your template. Doing so can cause the build process to fail with all sorts of odd messages. However, you also see the following message, which tells you precisely where the problem lies:

error: 'float' is not a valid type for a template non-type parameter

Tip Starting with C++ 11, you can use std::nullptr_t; as a parameter type, as in template <std::nullptr_t N>. When working with C++ 20, you gain access to these types as well:

  • Floating-point type
  • Literal class type with the following properties:
    • All base classes and nonstatic data members are public and non-mutable.
    • The types of all base classes and non-static data members are structural types. Depending on the compiler, you might also be able to use a multidimensional array of the structural type.

Tip A null pointer represents a special case that differentiates between 0 and an actual null (missing) value. Its actual representation is (void *)0, which makes it different from the C/C++ NULL value. You can see the null pointer discussed at https://hackernoon.com/what-exactly-is-nullptr-in-c-94d63y6t and https://stackoverflow.com/questions/13665349/what-is-a-proper-use-case-of-stdnullptr-t-template-parameters.

Including multiple parameters

You’re not limited to only one parameter when you create a template. For example, the Standard C++ Library has a template called map. The map template works like an array, but instead of storing things based on an index as you would in an array, you store them based on a key and value pair. To retrieve an item from map, you specify the key, and you get back the value. When you create a class based on the map template, you specify the two types map will hold, one for the key and one for the value. These are types, rather than objects or instances. After you specify the types, the compiler creates a class, and inside that class you can put the instances.

To show how this works, instead of using the actual map template, the following example creates a template that works similarly to a map. Instances of classes based on this template will hold only as many items as you specify when you create the class, whereas a real map doesn’t have any limitations beyond the size of the computer’s memory. The MultipleParameters example, shown in Listing 5-5, demonstrates an alternative map template.

LISTING 5-5: Using Multiple Parameters with Templates

#include <iostream>

using namespace std;

template<typename K, typename V, int S>
class MyMap {
protected:
K key[S];
V value[S];
bool used[S];
int Count;

int Find(K akey) {
int i;
for (i=0; i<S; i++) {
if (used[i] == false)
continue;
if (key[i] == akey) {
return i;
}
}
return -1;
}

int FindNextAvailable() {
int i;
for (i=0; i<S; i++) {
if (used[i] == false)
return i;
}
return -1;
}

public:
MyMap() {
int i;
for (i=0; i<S; i++) {
used[i] = false;
}
}

void Set(K akey, V avalue) {
int i = Find(akey);

if (i > -1) {
value[i] = avalue;
}
else {
i = FindNextAvailable();

if (i > -1) {
key[i] = akey;
value[i] = avalue;
used[i] = true;
}
else
cout << "Sorry, full!" << endl;
}
}

V Get(K akey) {
int i = Find(akey);

if (i == -1)
return 0;
else
return value[i];
}
};

int main() {
MyMap<char,int,10> mymap;

mymap.Set('X',5);
mymap.Set('Q',6);
mymap.Set('X',10);

cout << mymap.Get('X') << endl;
cout << mymap.Get('Q') << endl;
return 0;
}

When you run this application, you see this output:

10
6

This listing is a good exercise — not just for your fingers as you type it in, but for understanding templates. Notice the first line of the template definition:

template<typename K, typename V, int S>

This template takes three parameters. The first is a type, K, used as the key for map. The second is a type, V, used as the value for map. The final is S, and it’s not a type. Instead, S is an integer value; it represents the maximum number of pairs that map can hold.

The methods that follow allow the user of any class based on this map to add items to map and retrieve items from map. The example currently lacks functions for removing items; you might think about ways you could add such functions. You might even look at the header files for the map template in the Standard C++ Library to see how the designers of the library implemented a removal system.

Working with non-type parameters

Starting with C++ 11, you can use non-type parameters to define a template. The use of non-type parameters makes it possible to create templates that accept some interesting types of input, yet are more specific in some ways than general template types. Previous sections have shown how to use types for templates; here are some common non-types used for templates:

  • lvalue reference
  • nullptr
  • pointer
  • enumeration
  • integral
  • auto (some functionality provided starting with C++ 17 and enhanced with deduction of the class type in C++ 20)

One of the more interesting non-type parameters is an enumeration. You can use the enumeration to enforce things like kind selection or for verifying that a particular kind is in use. It also comes in handy for comparisons. The NonTypeParm example, shown in Listing 5-6, demonstrates techniques you can use when working with templates that rely on an enumeration.

LISTING 5-6: Using an Enumeration in a Template

#include <iostream>

using namespace std;

enum StoreType {
Red,
Blue,
Green
};

template <typename V>
struct StoreOut {
V Value;
StoreType Kind;
};

template <StoreType K, typename V>
class StoreIt {
protected:
V Value;
StoreType Kind = K;
public:
StoreIt() {
Value = 0;
}

StoreIt(V value) {
Value = value;
}

StoreOut<V>& getx();
void setx(StoreType, V);
string KindToString();
};

template <StoreType K, typename V>
StoreOut<V>& StoreIt<K, V>::getx() {
StoreOut<V>* Out = new StoreOut<V>();
Out->Value = Value;
Out->Kind = Kind;
return *Out;
}

template <StoreType K, typename V>
void StoreIt<K, V>::setx(StoreType newT, V newV) {
Value = newV;
Kind = newT;
}

template <StoreType K, typename V>
string StoreIt<K, V>::KindToString(){
switch (Kind) {
case Blue: return "Blue";
case Green: return "Green";
case Red: return "Red";
}
return "Not Found";
}

int main() {
StoreIt<StoreType::Blue, int> Test;
Test.setx(StoreType::Red, 5);

StoreIt<StoreType::Red, int> Test2(6);

cout << Test1.KindToString() << "\t" <<
Test1.getx().Value << endl;
if (Test1.KindToString() != "Blue")
cout << "Test1 storage type changed." << endl;
if (Test1.KindToString() == Test2.KindToString())
cout << "Test1 and Test2 are of equal types." << endl;
return 0;
}

This example stores two values: a storage type and a value. The StoreType enumeration contains the only values you can use as input: Red, Blue, and Green. You provide one of these values when creating the initial object and again when setting a value using setx(). Using this approach limits the number of object types that a caller can create to those that you expect.

Tip The type could be anything. For example, if you create a car object and your company only supports certain paint colors, you could limit selection to those colors programmatically. This example simplifies the enumeration selection so that you can more easily see how it works.

Because the example stores two values, it needs a method for returning two values, which is the purpose of the StoreOut structure. The getx() method uses it to return data to the caller.

The actual StoreIt class declaration protects the two variables: Value, which can be of any type; and Kind, which must be a StoreType enumeration value. It also provides three methods: getx(), which returns a StoreOut structure;, setx(), which accepts the StoreType and value used to set the object values; and KindToString(), which provides the utility service of changing a StoreType value to a string for output. The setx() and getx() methods work much the same as their counterparts in Listing 5-2. The KindToString() method uses a simple switch statement to perform the required translation.

The code in main() creates a StoreIt object, Test1, stores data in it, and then displays the values onscreen. The main() code also demonstrates some of the ways in which you might use this template class. For example, you could determine whether the Kind of Test1 has changed. You could also determine whether Test1 and Test2 are the same Kind of object. Notice that Test1 and Test2 use different constructor types so that the Kind is created as part of the template, but Value is either a default value of 0 or a specific value of 6 in this case. Here’s what you see as output:

Red 5
Test1 storage type changed.
Test1 and Test2 are of equal types.

Typedefing a Template

If there’s a template that you use with particular parameters repeatedly, often just using typedef is the easiest way to go. For example, if you have a template like this

template <typename T>
class Cluck {
public:
T Chicken;
};

and you use Cluck <int> repeatedly, employ the following:

typedef Cluck<int> CluckNum;

Then, anytime you need to use Cluck<int>, you can use CluckNum instead. Here’s how:

int main() {
CluckNum foghorn;
foghorn.Chicken = 1;
return 0;
}

Tip Using typedef for templates makes the resulting class name look like a regular old class name, rather than a template name. In the preceding example, you use CluckNum instead of the somewhat cryptic Cluck<int>. And interestingly, if you’re working as part of a team of programmers and the other programmers aren’t as knowledgeable about templates as you are, they tend to be less intimidated if you typedef the template.

Remember When the compiler creates a class based on a template, people say that the compiler is instantiating the template. Even though most people use the word instantiate to mean creating an object based on a class, you can see how the template itself is a type from which you can create other types. Thus, a class based on a template is actually an instance of a template, and the process of creating a class based on a template is called template instantiation.

Deriving Templates

If you think about it, you can involve a class template in a derivation in at least three ways. You can:

  • Derive a class from a class template
  • Derive a class template from a class
  • Derive a class template from a class template

If you want to find out about these techniques, read the following sections.

Deriving classes from a class template

You can derive a class from a template, and in doing so, specify the parameters for the template. In other words, think of the process like this:

  1. From a template, you create a class.
  2. From that created class, you derive your final class.

Suppose you have a template called MediaHolder, and the first two lines of its declaration look like this:

template <typename T>
class MediaHolder

Then you could derive a class from a particular case of this template, as in this header for a class:

class BookHolder : public MediaHolder<Book>

Here you create a new class (based on MediaHolder) called MediaHolder<Book>. From that class, you derive a final class, BookHolder. The ClassFromTemplate example, shown in Listing 5-7, is an example of the class MediaHolder.

LISTING 5-7: Deriving a Class from a Class Template

#include <iostream>

using namespace std;

class Book {
public:
string Name;
string Author;
string Publisher;
Book(string aname, string anauthor, string apublisher) :
Name(aname), Author(anauthor), Publisher(apublisher){}
};

class Magazine {
public:
string Name;
string Issue;
string Publisher;
Magazine(string aname, string anissue,
string apublisher) :
Name(aname), Issue(anissue), Publisher(apublisher){}
};

template <typename T>
class MediaHolder {
public:
T *array[100];
int Count;
void Add(T *item)
{
array[Count] = item;
Count++;
}
MediaHolder() : Count(0) {}
};

class BookHolder : public MediaHolder<Book> {
public:
enum GenreEnum
{childrens, scifi, romance,
horror, mainstream, hownotto};
GenreEnum GenreOfAllBooks;
};

class MagazineHolder : public MediaHolder<Magazine> {
public:
bool CompleteSet;
};

int main() {
MagazineHolder dl;
dl.Add(new Magazine(
"Dummies Life", "Vol 1 No 1", "Wile E."));
dl.Add(new Magazine(
"Dummies Life", "Vol 1 No 2", "Wile E."));
dl.Add(new Magazine(
"Dummies Life", "Vol 1 No 3", "Wile E."));
dl.CompleteSet = false;
cout << dl.Count << endl;

BookHolder bh;
bh.Add(new Book(
"Yellow Rose", "Sandy Shore", "Wile E."));
bh.Add(new Book(
"Bluebells", "Sandy Shore", "Wile E."));
bh.Add(new Book(
"Red Tulip", "Sandy Shore", "Wile E."));
bh.GenreOfAllBooks = BookHolder::childrens;
cout << bh.Count << endl;
return 0;
}

When you run this example, you see the magazine count of 3 first, and the book count of 3 second.

Deriving a class template from a class

A template doesn’t have to be at the absolute top of your hierarchy; a template can be derived from another class that’s not a template. When you have a template and the compiler creates a class based on this template, the resulting class will be derived from another class. For example, suppose you have a class called SuperMath that isn’t a template. You could derive a class template from SuperMath. The TemplateFromClass example, shown in Listing 5-8, demonstrates how you can do this.

LISTING 5-8: Deriving a Class Template from a Class

#include <iostream>

using namespace std;

class SuperMath {
public:
int IQ;
};

template <typename T>
class SuperNumber : public SuperMath {
public:
T value;

T &AddTo(T another) {
value += another;
return value;
}

T &SubtractFrom(T another) {
value -= another;
return value;
}
};

void IncreaseIQ(SuperMath &inst) {
inst.IQ++;
}

int main() {
SuperNumber<int> First;
First.value = 10;
First.IQ = 206;
cout << First.AddTo(20) << endl;

SuperNumber<float> Second;
Second.value = 20.5;
Second.IQ = 201;
cout << Second.SubtractFrom(1.3) << endl;

IncreaseIQ(First);
IncreaseIQ(Second);
cout << First.IQ << endl;
cout << Second.IQ << endl;
return 0;
}

The base class is called SuperMath, and it has a member called IQ. From SuperMath, the example derives a class template called SuperNumber that does some arithmetic. Later, the example adds an Incredible IQ-Inflating Polymorphism to use in this function:

void IncreaseIQ(SuperMath &inst) {
inst.IQ++;
}

Note what this function takes as a parameter: A reference to SuperMath. Because the SuperNumber class template is derived from SuperMath, any class you create based on the template is, in turn, derived from SuperMath. That means that if you create an instance of a class based on the template, you can pass the instance into the IncreaseIQ() function. (Remember, when a function takes a pointer or reference to a class, you can instead pass an instance of a derived class.)

Deriving a class template from a class template

If you have a class template and you want to derive another class template from it, first you need to think about exactly what you’re doing; the process takes place when you attempt to derive a class template from another class template. Remember that a class template isn’t a class: A class template is a cookie-cutter that the compiler uses to build a class. If, in a derivation, the base class and the derived classes are both templates, what you really have is the following:

  1. The first class is a template from which the compiler builds classes.
  2. The second class is a template from which the compiler will build classes that are derived from classes built from the first template.

Now think about this: You create a class based on the base class template. Then you create a second class based on the second template. This process doesn’t automatically mean that the second class derives from the first class. Here’s why: From the first template, you can create many classes. When you create a class from the second template, which of those classes will it derive from?

To understand what’s happening here, look at the TemplateFromTemplate example, shown in Listing 5-9. To keep the code simple, the example uses basic names for the identifiers. (Notice that we commented out one of the lines. If you’re typing this, type that line in, too, with the comment slashes, because you’ll try something in a moment.)

LISTING 5-9: Deriving a Class Template from a Class Template

#include <iostream>

using namespace std;

template <typename T>
class Base {
public:
T a;
};

template <typename T>
class Derived : public Base<T> {
public:
T b;
};

void TestInt(Base<int> *inst) {
cout << inst->a << endl;
}

void TestDouble(Base<double> *inst) {
cout << inst->a << endl;
}

int main() {
Base<int> base_int;
Base<double> base_double;

Derived<int> derived_int;
Derived<double> derived_double;

TestInt(&base_int);
TestInt(&derived_int);
TestDouble(&base_double);
TestDouble(&derived_double);

//TestDouble(&derived_int);
return 0;
}

Remember The example has two functions, each taking a different class — and each class based on the first template, called Base. The first takes Base<int> * as a parameter, and the second takes Base<double> * as a parameter. When a function, such as TestInt() or TestDouble(), takes a pointer to a class, it can legally pass a pointer to an instance of a derived class, which means that you can create this variable:

Derived<int> derived_int;

You pass this variable to the function that takes a Base<int> and it compiles. That means that Derived<int> is derived from Base<int>. In the same way, Derived<double> is derived from Base<double>. When you run this code, it outputs four numbers: two int values and two double values.

To see how Derived<int> relies on Base<int>, uncomment the line TestDouble(&derived_int). When you do this, and you try to compile the listing, you see this message:

error: cannot convert 'Derived<int>*' to 'Base<double>*' for argument '1' to 'void TestDouble(Base<double>*)'

The error message says you can’t pass a pointer to Derived<int> to a function that takes a pointer to Base<double>. That’s because Derived<int> isn’t derived from Base<double>.

Remember Templates aren’t derived from other templates. You can’t derive from templates because templates aren’t classes. Rather, templates are cookie cutters for classes, and the class resulting from a template can be derived from a class resulting from another template. Look closely at the declaration of the second template class. Its header looks like this:

template <typename T>
class Derived : public Base<T>

The clue here is that the Derived template takes a template parameter called T. Then the class based on the template is derived from a class called Base<T>. But in this case, T is the parameter for the Derived template. See what happens if you create a class based on Derived, such as this one:

Derived<int> x;

This line creates a class called Derived<int>; then, in this case, the parameter is int. Thus the compiler replaces the Ts so that Base<T> in this case becomes Base<int>. So Derived<int> is derived from Base<int>.

Templatizing a Function

A function template is a function that allows the user to essentially modify the types used by a function as needed. For example, look at these two functions:

int AbsoluteValueInt(int x) {
if (x >= 0)
return x;
else
return -x;
}

float AbsoluteValueFloat(float x) {
if (x >= 0)
return x;
else
return -x;
}

To take the absolute value of an integer, you use the AbsoluteValueInt() function. But to take the absolute value of a float, you instead use the AbsoluteValueFloat() function. Of course, you need yet another function to support double or other types. Instead of having a separate function for double and a separate function for every other type, you can use a template like this:

template <typename T> T AbsoluteValue(T x) {
if (x >= 0)
return x;
else
return -x;
}

Now you need only one version of the function, which handles any numeric type, including double. The users of the function can, effectively, create their own versions of the function as needed. For example, to use an integer version of this function, you put the typename, int, inside angle brackets after the function name when calling the function:

int n = -3;
cout << AbsoluteValue<int>(n) << endl;

If you want to use the function for a float, you do this:

float x = -4.5;
cout << AbsoluteValue<float>(x) << endl;

Note the function template declaration. The real difference between the function template and a standard function is in the header:

template <typename T> T AbsoluteValue(T x)

Begin with the word template, a space, and an open angle bracket (that is, a less-than sign). These characters are followed by the word typename, a closing angle bracket (that is, a greater-than sign), and then an identifier name. Most people like to use the name T (because it’s the first letter in type). At this point, you add the rest of the function header, which, taken by itself, looks like this:

T AbsoluteValue(T x)

Remember T represents a type. Therefore, this portion of the function header shows a function called AbsoluteValue that takes T as a parameter and returns T. Creating a function based on this template by using an integer, means that the function takes an integer parameter and returns an integer. When the compiler encounters a line like this:

cout << AbsoluteValue<float>(x) << endl;

it creates a function based on the template, substituting float anywhere it sees T. However, if you have two lines that use the same type, as in this:

cout << AbsoluteValue<float>(x) << endl;
cout << AbsoluteValue<float>(10.0) << endl;

the compiler creates only a single function for both lines.

Overloading and function templates

If you really want to go out on a limb and create flexibility in your application, you can use overloading with a function template. Remember, overloading a function means that you create two different versions of a single function. What you’re doing is creating two separate functions that have different parameters (that is, either a different number of parameters or different types of parameters), but they share the same name. Look at these two functions found in the FunctionOverloadingAndTemplates example:

int AbsoluteValue(int x) {
if (x >= 0)
return x;
else
return -x;
}

float AbsoluteValue(float x) {
if (x >= 0)
return x;
else
return -x;
}

These functions are an example of overloading. They take different types as parameters. (One takes an int; the other takes a float.) Of course, you could combine these functions into a template:

template <typename T> T AbsoluteValue(T x) {
if (x >= 0)
return x;
else
return -x;
}

There really isn’t any difference between the two examples. After all, you can use the following two lines of code either after the overloaded functions (without the type parameters) or after the function template:

cout << AbsoluteValue<int>(n) << endl;
cout << AbsoluteValue<float>(x) << endl;

In this case, n is an int and x is a float. However, the template is a better choice. If you use the overloaded form and try this code, you see an error:

cout << AbsoluteValue(10.5) << endl;

Even though 10.5 is a float you see an error message like this:

error: call of overloaded 'AbsoluteValue(double)' is ambiguous

The message contains AbsoluteValue(double), which means that the compiler thinks that 10.5 is a double, not a float. You can pass a double into either a function that takes an int or a function that takes a float. The compiler will just convert it to an int or a float, whichever it needs. Because the compiler thinks that 10.5 is a double, it can pass the value to either overloaded function version. So that leaves you with a choice: You can cast it to a float using (float)10.5; declare it a float using 10.5f; or create a third overloaded version of the function, one that takes a double.

Creating a template is easier than overcoming these sorts of errors. The second reason the template version is better: If you want a new type of the function, you don’t need to write another version of the function.

However, you can also overload a function template. The OverloadedFunctionTemplate example, shown in Listing 5-10, contains an overloaded function template.

LISTING 5-10: Overloading a Function Template

#include <iostream>

using namespace std;

template <typename T> T AbsoluteValue(T x) {
cout << "(using first)" << endl;
if (x >= 0)
return x;
else
return -x;
}

template <typename T> T AbsoluteValue(T *x) {
cout << "(using second)" << endl;
if (*x >= 0)
return *x;
else
return -(*x);
}

int main() {
int n = -3;
cout << AbsoluteValue<int>(n) << endl;

float *xptr = new float(-4.5);
cout << AbsoluteValue<float>(xptr) << endl;
cout << AbsoluteValue<float>(10.5) << endl;
return 0;
}

Passing a pointer (as in the second call to AbsoluteValue() in main()), uses the second version of the template. And just to be sure which version gets used and at what time during application execution, the example contains a cout line at the beginning of each function template. Here’s what you see as output:

(using first)
3
(using second)
4.5
(using first)
10.5

From the middle two lines, you can see that the computer did indeed call the second version of the template.

Tip You can make life a little easier by using a small trick. Most compilers let you leave out the type in angle brackets in the function template call itself. The compiler deduces what type of function to build from the template, based on the types that you pass into the function call. Here’s an example main() that you can substitute for the main() in Listing 5-10:

int main() {
int n = -3;
cout << AbsoluteValue(n) << endl;
float *xptr = new float(-4.5);
cout << AbsoluteValue(xptr) << endl;
cout << AbsoluteValue(10.5) << endl;
return 0;
}

This code replaces AbsoluteValue<int>(n) with AbsoluteValue(n). When you run the modified code, you see the same output as when you run Listing 5-10.

Templatizing a method

When you write a template for a class, you can put function templates inside the class template. You simply declare a function template inside a class, as in the following found in the MemberFunctionTemplate example:

class MyMath {
public:
string name;
MyMath(string aname) : name(aname) {}

template <typename T> void WriteAbsoluteValue(T x) {
cout << "Hello " << name << endl;
if (x >= 0)
cout << x << endl;
else
cout << -x << endl;
}
};

The WriteAbsoluteValue() method is a template. It’s preceded by the word template and a template parameter in angle brackets. Then it has a return type, void, the function name, and the function parameter.

When you create an instance of the class, you can call the method, providing a type as need be, as in the following:

int main() {
MyMath inst = string("George");
inst.WriteAbsoluteValue(-50.5);
inst.WriteAbsoluteValue(-35);
return 0;
}

In the first call, the function takes a double (because, by default, the C++ compiler considers -50.5 a double). In the second call, the function takes an integer. The compiler then generates two different forms of the function, and they both become members of the class.

Remember Although you can use function templates as class members, you cannot make them virtual. The compiler won’t allow it, and the ANSI standard forbids you from doing it. If you try to make the function template virtual, you get an error message that looks similar to this one:

'virtual' can only be specified for functions