Creating Classes with Templates - Advanced Programming - C++ All-in-One For Dummies (2009)

C++ All-in-One For Dummies (2009)

Book IV

Advanced Programming

Chapter 5: Creating Classes with Templates

In This Chapter

Creating class templates

Using parameters in templates

Deriving with templates

Creating function templates

If C++ programming has any big secret, it would have to be templates. 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.

We can’t say whether understanding templates will make you a C++ guru (we like to think that it will!), but we can say that it will open your abilities to a whole world out there, 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. (And it will help you realize that it didn’t have to be so cryptic! Simplify, simplify, we always say!)

So, in this chapter, we show you how to program templates in C++.

imageTemplates have an interesting history. Back when people started building C++ compilers, no standard for how to do templates existed. As a result, different compilers supported different ways of doing templates. The whole template thing was a mess and very confusing. So if you’re using a compiler that’s older than around 1999 or 2000 and you’re interested in using templates, you should seriously consider upgrading to a newer compiler (such as the GNU gcc compiler included with CodeBlocks on the CD that comes with this book).

Templatizing a Class

Templates are complicated and difficult to understand. That was a lie. We don’t know why people make creating and using templates so hard, but they’re really not. In this section, we show you just how simple templates are to understand.

First, think of a class. Pick one, any class. We’ll pick OldGasStation. That’s a class, and it has some members. Remember, a class is a type. You can declare variables of the type. Thus, we can declare a variable of type OldGasStation called, for example HanksGetGas. We can also create another variable of type OldGasStation; maybe this one would be called FillerUp. And, of course, we could 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, we can take an existing type, say int, and make some instances of it. We can name one CheckingAccountBalance, and we can name another BuriedTreasuresFound. Each of these is an instance of the type called int. Although int isn’t a class, it is a type.

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

We want to focus on the one you create, OldGasStation. This is a type that you create by declaring it in your program when you write the code. The compiler takes your declaration and builds some data inside the resulting program that represents this type. After the program runs, the type is created, and it does not change throughout the course of the program.

image The variables in your program 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 does not change at runtime. Remember this as one property of types in general. You will need to keep this in mind when dealing with templates.

Suppose that you have a class called MyHolder. This class is going to 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 program 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 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 creates an instance at runtime, does some work with it, and then deletes the instance. It then repeats this over and over for a total of ten times. Instances (or variables) are created, changed, and deleted — all at runtime. But the class, which we’ll say one more time, is created at compile time.

Suppose you’re coding away, and you discover that this class MyHolder is pretty 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 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, 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.

Take a 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 as a rule for a class that does exactly what the previous two classes did. In this rule is a placeholder called T that is a placeholder for a type. Imagine, in your mind, this set of code; then remove the first line and replace all the remaining Ts 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;

}

};

And once again, this is, of course, 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 take this template and create a class. The way you do this is 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;

Do you see what’s happening? This code is telling the compiler to take the CoolHolder template and make a version of it where T is replaced by the word int. In other words, the compiler creates a class. What is the class called? It’s called CoolHolder<int>. And then these four lines of code first create an instance of CoolHolder<int> called IntHolder; then they set the members of IntHolder.

And when does the computer create this class? (That is, not the instance, but the class itself?) At compile time. Remember, types are created at compile time, and this is no exception to this rule.

imageHere’s an easy way to look at a template. When you see a line like CoolHolder<int> IntHolder; you can think of it 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>.)

Listing 5-1 shows a complete program 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 program, 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 that we showed you earlier. 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.

Here’s the first line of the template:

template <typename T>

All this means is that a template class is going to follow, and that it has a type with a placeholder called T. That means that inside the class anywhere a T appears, it will be replaced by the typename. (The T is standalone; 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).

Down inside the main for this class, we then 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 we declare 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.

Here we create another class based on the template, and we declare a variable of this new type:

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 three members of the instance FloatHolder, called first, second, and third, each hold a floating-point number. And the member function called sum returns a floating-point number.

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

CoolHolder<int> *hold;

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

hold = new CoolHolder<int>;

We access the members using the pointer notation, ->, like so:

hold->first = loop * 100;

And that’s 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. So see what you think of this line of code:

vector<string> MyList;

Any idea what this code does? It uses some template called vector and tells vector to use the string type inside it. In fact, vector is part of the Standard C++ Library, and it works similar to an array. Its template parameter (the thing in angle brackets) represents the type of the items the vector holds. So this declares a variable called MyList, which is a vector that holds string instances.

Separating a template from the function code

In the earlier days of templates and C++, the rule was that you had to put member function code for a class template inside the template itself; you couldn’t put a forward declaration and then put the function code outside the template as you could do with classes. However, the ANSI standard changed this and made putting the code outside the template legal. If you are using an ANSI-compliant compiler, you can put the function code outside the template. The GNU gcc compiler is, for the most part, ANSI-compliant; with it, you can put the code outside the template. However, you have to place the code carefully to get it to compile correctly. The code in Listing 5-2 shows you how to do this.

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;

}

As you can see, the format is ugly. To be honest, whenever we do this, we have to look up the format; it’s hard to memorize. (Maybe you can memorize it, but our brains are too prone to explosions.)

Look closely at one of the member functions:

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 classname 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 by two angle brackets, with the parameter inside, like this: T &ImFree<T>::getx(). Note the <T> part.

image Earlier compilers didn’t allow you to separate the function code the way we did in Listing 5-2. Instead, you would have to put the function code inside the template itself, as in the following:

template <typename T>

class ImFree

{

protected:

T x;

public:

T& getx()

{

return x;

}

void setx(T newx)

{

x = newx;

}

};

imageNote one little thing that we did in both Listing 5-2 and in the old type of code: For the getx member function, instead of just returning a variable of type T, we returned a reference. That is, instead of this:

T getx()

we declared the function as

T& getx()

(We added the ampersand.) Although that has the potential of upsetting some people, there’s a good reason for doing it. In the main of Listing 5-2, we created the class based on the template with an integer parameter:

ImFree<int> separate;

However, we could instead create the class with some other class:

ImFree<SomeOtherClass> separate;

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

T& getx()

{

return x;

}

Returning just an instance copies the instance rather than just returning the instance itself. The solution might be to use a pointer, as in

T* getx()

{

return &x;

}

And, in fact, we tried that when we first wrote the code. But then the code gets annoying because we would have to dereference the result when we use the function, even if the result is just an integer. Our cout statement ended up looking like this when we used a pointer:

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

And frankly, we found that code, shall we say, yucky. So, as in a presidential election, we picked the lesser of the evils by making it a reference. We figured a reference was less evil because the user of the class wouldn’t have to do any bizarre coding. Instead, the 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. And the easy way is exactly how this process works.

image 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.

Listing 5-3 shows 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;

}

First, see how we declared the storage for the static member; it’s the two lines in between the template and main. The syntax is somewhat difficult to remember: First, you supply the same template header you would for the class. (That is, notice that the line template <typename T> appears both before the class template and the storage line.) Then, you specify the type of the static member (in this case T, which is the template parameter). Next, you refer to the static member by using the usual class name::member name syntax. But remember that the class name gets the template parameter in angle brackets after it. Done deal.

In this code, you can also see that we created two classes based on the templates Electricity <int> and Electricity <float>. Each of these classes has its own instance of the static member; for the <int> version, we put a 10 in it, and for the <float> version, we put a98.6 in it. Then, just to show that there’s only a single static member per class, we created an instance of Electricity<int> and set its static member to 22. Then we wrote them to the console with the cout statement. And, indeed, the two lines for Electricity<int> are the same, and the one for Electricity<float> is different from the two for Electricity<int>. Done deal!

Parameterizing a Template

A template consists of a template name followed by one or more parameters inside angled brackets. Then comes the class definition. When you politely ask the compiler to create a new class based on this template, the compiler happily obliges by making a substitution for whatever you supply as the parameter. At least we think that the compiler is happy. It doesn’t complain much beyond the occasional error message.

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. Life is simple sometimes.

But what we want you to notice in particular is what’s inside the angled brackets. This is the parameter: typename T. Like parameters in a function, first is the type of the parameter (typename), and second is the name of the parameter (T).

But is typename a, um, typename? Not really; it’s a special C++ word reserved for use in templates. typename means that what follows (in this case, T) is a type. So when you politely tell the compiler to create a new class based on this template, you specify a type for T. For example, this line tells the compiler to create the new class and make a variable named JustForMe that is of the new class:

SomethingForEveryone<int> JustForMe;

Now the compiler looks at what you supplied inside angle brackets and uses that as a parameter for the template. Here, int goes with the T in the template. The compiler will take each instance of T that isn’t inside a word, and replace it with the parameter, which is int.

Putting different types in the parameter

It turns out there’s more to this parameter thing than meets the computer screen. You can put many more things 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;

}

}

};

Before we show you an example that uses this template, we’ll quickly explain what’s going on with it. This time, the template parameter isn’t a type at all — it’s an integer value, an actual number. Then, inside the class, we use that number as a constant. As you can see in theTestPrice function, we compare the Price member to the constant, which is called MaxPrice. So this time, instead of using T for the name of the template parameter, we used something a little more sensible, MaxPrice. And MaxPrice is a value, not a type.

Listing 5-4 shows 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()

{

if (Price > MaxPrice)

{

cout << “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();

PriceController<FredMaxPrice> FredsDrawingSet;

FredsDrawingSet.Price = 45;

FredsDrawingSet.TestPrice();

const int JulieMaxPrice = 60;

PriceController<JulieMaxPrice> JuliesCar;

JuliesCar.Price = 80;

JuliesCar.TestPrice();

return 0;

}

Each person gets a different class. 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. And 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>, we created two instances of that class. And for the second one, PriceController <JulieMaxPrice>, we created one instance.

image The compiler really does create two separate classes, one called PriceController <FredMaxPrice> and one called PriceController <JulieMaxPrice>. These are as separate as they would be if you typed in two separate classes, one calledPriceControllerFredMaxPrice and one called PriceControllerJulieMaxPrice. They aren’t separate instances of a class — they are separate classes.

So far in this section, we’ve shown you that you can use a type as a template parameter or a value of a certain type. You can also use a class as a template parameter. The following list describes each type of parameter:

Value parameters: (The ANSI standard calls these non-type parameters, but we like value better.) You can give the type and name for a value in the parameter, as in template <int MaxPrice>. But for some reason, the ANSI standard forbids you from using a floating-point value here, as in template <float MaxPrice>, or a class, as in template <MyClass inst>, or a void type, as in template <void nothing>. But you’re free to use pointers, so template <float MaxPrice> is allowed, and so are template <MyClass *inst> and template <void *MaxPrice>. (Although, in general, you should avoid void * because it’s not very useful; try to be more specific with your pointers, as in int *MaxPrice.)

♦ typename parameters: You can use a type as a parameter to a class, as in template <typename T>. You then use a type when you ask the compiler to create the class based on the template. And when you use typename, you have to make sure that you actually use it as a type inside the class; don’t just pass a variable for the parameter.

Class parameters: Remember that a class is in itself a type, so you can pass a classname when your template requires a type. But remember that we’re not talking about passing an instance of a class to the template; We’re talking about passing a class itself by specifying its name in the template parameter.

Parameterizing with a class

When your template is expecting a class for its parameter (remember, a class, not an instance of a class), you can use the word typename in the template parameter as we did in the examples in this chapter. You would then instruct the compiler to create a class based on the template by passing a classname into the template, as in MyContainer<MyClass> inst;. Typically, you would use a class, called a container, as a template parameter if you have a template that you intend to hold instances of a class. However, instead of using the word typename, you can instead use the word class, like so:

template <class T>

class MyContainer

{

public:

T member;

};

But whichever you use, typename or class, really doesn’t matter: According to the C++ ANSI standard, the word typename and the word class are interchangeable when used in a template parameter.

image The GNU gcc compiler that’s used for CodeBlocks corrects a strange error message that other compilers sometimes provide when you use something you’re not supposed to inside a template. The problem is, these older compilers don’t give an error message for the line that has the word template in it, such as template <float MaxPrice>; instead, they give two error messages for the line that tries to create the class based on the template, such as PriceController<FredMaxPrice> FredsToaster;. Here are the two messages we saw:

non-constant `FredMaxPrice’ cannot be used as template argument

ANSI C++ forbids declaration `FredsToaster’ with no type

When working with CodeBlocks, you still see the message associated with the class creation. 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 constant parameter

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 an object called a key. In other words, you store things in an array in pairs; the first item in the pair is called a key and the second item is called a value. 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. Note that we said the types that map will hold, not the objects or instances it will hold. 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, we’re going to make our own 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. These days that means map can hold just about as much as you want! So load up on map items! Listing 5-5 shows our 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 program, you will see this output:

10

6

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

template<typename K, typename V, int S>

This template takes not one, not two, but (count them!) three parameters. The first is a type, and we use it as the key for map, so we call it K. The second is a type, and we use it as the value for map, so we call it V. 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 member functions that follow allow the user of any class based on this map to add items to map and retrieve items from map. We didn’t include any functions for removing items; you might think about ways you could add such an item. You might even take a 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.

Typedefing a Template

If there’s a template that you use with particular parameters over and over, often just using typedef for the thing 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 find yourself using Cluck <int> over and over, you can typedef this, as in the following:

typedef Cluck<int> CluckNum;

Then, anytime you need to use Cluck<int>, you can use CluckNum instead. This main demonstrates the use of ClickNum:

int main()

{

CluckNum foghorn;

foghorn.Chicken = 1;

return 0;

}

imageWe like to typedef our templates, because then the classname looks like a regular old classname, rather than a template name. In the preceding example, we get to use the classname 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. That way, you get to use a regular name, and they won’t have a brain overload when they see your code. But don’t tell them we said that.

image When the compiler creates a class based on a template, people say that the compiler is instantiating the template. we know, we know, most people use the word instantiate to mean that you create an object based on a class. But if you stretch your imagination, you can see how the template itself is a type from which you can create other types. And 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.

imageWhen you use a typedef to give a simpler name to a specific class based on a template, the compiler instantiates the class based on the template. Or, to put it another way, the compiler instantiates the template class.

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

Or you could do none of these three items. But if you want to find out about them, read the following sections, where we show you how they work.

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 it like this, if it’s not too roundabout: From a template, you create a class, and 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 we created a new class (based on MediaHolder) called MediaHolder<Book>. And from that class, we derived our final class, BookHolder. Listing 5-6 is an example of the class MediaHolder, and the listing includes some good books and magazines to add to your reading list as well.

Listing 5-6 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(

“CEOing for Dumdums”, “Gookie Dan”, “Wile E.”));

bh.Add(new Book(

“Carsmashing for Dumdums”, “Woodie and Buzz”,

“Wile E.”));

bh.Add(new Book(

“Turning off the Computer for Dumdums”,

“Wrath of Andy”,

“Wile E.”));

bh.GenreOfAllBooks = BookHolder::hownotto;

cout << bh.Count << endl;

return 0;

}

Deriving a class template from a class

A template doesn’t have to be at the absolute top of your hierarchy, the total king of the hill. No, a template can be derived from another class that’s not a template. The brain acrobatics work like this: 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 is not a template. You could derive a class template from SuperMath. Listing 5-7 shows how you can do this.

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

#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;

}

Note something really great that we did here! The base class is called SuperMath, and it has a member called IQ. From SuperMath, we derived a class template called SuperNumber that does some arithmetic. Later, we put an Incredible IQ-Inflating Polymorphism to use in this function:

void IncreaseIQ(SuperMath &inst)

{

inst.IQ++;

}

Note what this function takes: A reference to SuperMath. Because the SuperNumber class template is derived from SuperMath, that means any class we create based on the template is, in turn, derived from SuperMath. And that means that if we have an instance of a class based on the template, we 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. Not like what you’re doing spending all your days programming when you could be out enjoying the sunshine; rather, what we mean is, “What 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, really what you 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. Does this automatically mean that the second class is derived from the first class? Nope! Here’s why: From the first template, you can create many classes. Now if you create a class from the second template, which of those many classes will it be derived from?

To understand what is happening, take a look at Listing 5-8. To keep the code simple, we put the jokes aside and just gave the identifiers basic names. (And notice that we commented one of the lines out. If you’re typing this, go ahead and type that line in, too, with the comment slashes, because we want you to try something in a moment.)

Listing 5-8: 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;

}

Now compile the program. The preceding 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.

image If a function takes a pointer to a class, we can legally pass a pointer to an instance of a derived class. Now note that we created this variable:

Derived<int> derived_int;

And we pass this variable to the function that takes a Base<int>. And it compiles. That means Derived<int> is derived from Base<int>. In the same way, Derived<double> is derived from Base<double>.

Now just to make sure that this is correct, if you look at the commented out line, you should see that if you uncomment the line, it should not compile. Go ahead and try uncommenting 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>*)’

Thus, 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>. Yet, it would appear from the code that the template is derived from the template for Base<double>. But that’s not true. Here’s why.

image Templates are not derived from other templates. You can’t derive templates because templates are not 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.

Now that we have that cleared up, look closely at how we declared 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.

So if we create a class based on Derived, such as this one:

Derived<int> x;

We just created a class called Derived<int>; then, in this case, the parameter is int. And thus, the compiler replaces the Ts so that Base<T> in this case becomes Base<int>. And so Derived<int> is derived from Base<int>.

And that’s how this template derivation stuff works!

image When you derive a template class from another template class, you actually make use of the template parameter, and that gets passed into the base class template as a parameter.

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, take a 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;

}

If the user of the functions needs to take the absolute value of an integer, he or she would use the AbsoluteValueInt function. But to take the absolute value of a float, he or she would instead use the AbsoluteValueFloat function. What about a double? Or some other numerical type?

But instead of having a separate function for double and a separate function for every other type, we would suggest using a template, as in 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 they need them. For example, to use an integer version of this function, we just put the typename,int, inside angle brackets after the function name when calling the function:

int n = -3;

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

And if we want to use the function for a float, we just do this:

float x = -4.5;

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

Note how we declared the function template itself. The real difference between the function template and a regular run-of-the-mill function is in the header:

template <typename T> T AbsoluteValue(T x)

First we put the word template. Then we follow it with any number of optional spaces, and then an open angle bracket (that is, a less-than sign). Following the angle bracket, we put the word typename, a close angle bracket (that is, a greater-than sign), and then an identifier name. Most people like to use the name T (since it’s the first letter in type), so that’s what we did, being ones to follow the crowd. Then we put 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. So if we create a function based on this template by using an integer, the function will take an integer as a parameter and return an integer. Remember, T is basically a placeholder for a typename. So 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 only creates a single function for both lines. (And these two lines don’t need to be one after the other.)

Overloading and function templates

If you really want to go out on a limb and create flexibility in your program, you can use overloading with a function template. Remember, overloading a function means that you create two different versions of a single function. Really, 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:

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, and 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;

}

But is this really any different? After all, you can use the following two lines of code either after the overloaded functions or after the function template:

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

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

(We’re assuming that n is an integer and x is a float.) However, the template is a better choice. First, if you use the overloaded form and then try this, you’ll get a problem:

cout << AbsoluteValue(10.5) << endl;

We all know that 10.5 is a float; therefore, the compiler should just call the float version of the overloaded function. However, the GNU gcc compiler that ships with CodeBlocks gives us this error message:

error: call of overloaded `AbsoluteValue(double)’ is ambiguous

Ambiguous? But look! The message is saying AbsoluteValue(double)! Hmmm . . . Apparently, the GNU gcc compiler thinks that our 10.5 is a double, not a float. And you can actually 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. And because the compiler thinks that 10.5 is a double, it figures it can pass it to either version of the overloaded function. So that leaves you a choice: You can either cast it to a float or create a third overloaded version of the function, one that takes a double.

Yuck. At least CodeBlocks gives you some clue as to the problem because the extended error information is

note: candidates are: int AbsoluteValue(int)

note: float AbsoluteValue(float)

Creating a template is easier. And that brings us to 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.

But what if you want to overload a function template? That sounds kind of scary, but you can do it. Listing 5-9 shows an overloaded function template.

Listing 5-9: Overloading a Function Template Provides Even Greater Flexibility

#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;

}

When we pass a pointer (as in the second call to AbsoluteValue in main), the compiler figures out that it needs to use the second version of the template. And just to be sure which version gets used and at what time during application execution, we threw in a cout line at the beginning of each function template. When you run this code, here’s what you see:

(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.

imageYou can make life a little easier by using a little trick. Most compilers let you leave out the type in angle brackets in the function template call itself. The compiler is smart enough to figure out what type of function to build from the template, based on the types that you pass into the function call! Pretty wild. Here’s an example main that you can substitute for the main in Listing 5-9:

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;

}

In this code, we replaced AbsoluteValue<int>(n) with just AbsoluteValue(n). When you run the modified Listing 5-9, you see the same output as when you run Listing 5-9.

Templatizing a member function

When you write a template for a class, you can put function templates inside the class template. To people who were familiar with some of the early versions of C++ where template support was minimal, this seems a little shocking. But when the electrical current wears off, we all see that putting a template function inside a template class is possible. You simply declare a function template inside a class, as in the following:

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 member function 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 member function, 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.

image Although you can use function templates as class members, you cannot make them virtual. The compiler will not allow it, and the ANSI standard forbids (“Forbids, we say!”) you from doing it. In fact, we tried it with CodeBlocks just to see what friendly message we would get. Here it is:

`virtual’ can only be specified for functions

We guess by functions it means actual functions — not function templates.