Selection sort, as written, does not rely on the elements of data being of type int, it only requires that the elements of data be comparable (i.e. have a < operator defined for them), allow them to b initialized (think default constructor) and assigned to. Integers have these properties, but so do a host of other types.
Goal: parameterize sort so that it can operate on arrays of any types which support those basic operations.
How do we do this? A simple idea is to use the typedef mechanism. The statement:
typedef t foomakes foo a synonym for the type t (whatever type t is). In our case, we might write:
typedef int ElementTypeand then rewrite sort using ElementType in place of some or all of occurrences of int.
Now if we want to change this sort function to operate on arrays of strings instead, we just change one line:
typedef String ElementType
This has its limitations: what happens if we want to use an int sort in one part of our program and a String sort in another?
A more flexible solution is to use function templates. A template is, like it sounds, a form where "blanks" get filled in at a later date. For sort, template function looks like:
template <class T> void SelectionSort(T A[], int N) { T min; int j, k, minIndex; for (k = 0; k < N; k++) { min = A[k]; minIndex = k; for (j = k+1; j < N; j++) { if (A[j] < min ) { min = A[j]; minIndex = j; } } Swap(A[k], A [minIndex]); } }
How do templates work? Each time a template function is called - the types of the arguments to the function are plugged into the template. The compiler generates one version (an instantiation) of the template function for each set of types used when calling the template function. For example, consider the power function:
template <class Base> Base power(Base b, int n) { B result = b; for(; n > 0; n--) result = result * b; return result; } power(10,4) // uses int power power(3.14,2) // uses double power power(Complex(0,1),2) // uses Complex power, assuming power("hello",2) // error - // string has no multiplication operator defined
How should function templates be written?
A caveat: Each template type parameter must appear in one of the function specifications (not including return type). If not, compiler does not know how to "resolve" the type parameter. For example:
// valid: template <class T> void swap(T& A, T& B) { T temp; temp = A; A = B; B = temp; } // invalid: template <class T> T donothing() { T temp; temp = A; A = B; B = temp; }
When discussing bags, we have focused on a Bag class that has elements of type int. We observed that nothing in our implementation of the Bag class depends on int. When discussing lists, we have focused on the StringList class - a list of strings. Likewise, this class is not dependent on the type String. We would like to be able generate a bag of strings, a bag of ints, a bag of int bags, a bag of string lists, and bags of items of virtually any type all from from the same Bag code. We would also like to be able to generate a list of ints, a list of string lists, a list of int list bags, etc. all from the same List code.
How can we accomplish this? Like in the case of functions that can be generalized to operate on a variety of types, classes can be parameterized in several ways:
class Bag { typedef int Item; ... Item data[CAPACITY]; ... };The limitation with this approach is the same as the limitation with functions parametrized using typedefs - what if we want to use two different kinds of lists within the same program?
template <class Item> class List { ... Item data[CAPACITY]; ... };Programming Assignment One makes extensive use of a the List class template (implemented using linked lists). For further examples of list class you should examine the code list.h and list.C related to that assignment.
Member functions for class templates are themselves function templates. For example:
template <class Item> bool Bag<Item>::isEmpty() const { return (used==0); }and
template <class Item> Item List<Item>::current() const // Returns the item pointed to by the cursor { assert(!isEnd()); return location->val; }
As when writing function templates, it is better to write the class for a fixed type first (perhaps using typedefs). Compile and test your implementation thoroughly before converting it to a class template.
A class template is not a class (nor a valid type). Thus if List is a class template, we cannot write:
List L;(However, List() remains the name of the constructor for all instantiations of the List class template.)
We can write expressions of this form:
List<int> iL; List<List<String>> sLL;but not:
List<List> LL;However, we can combine the use of typedefs with class templates to useful effect:
typedef List<int>IntList; typedef List<IntList> ListList;
A quirk of the design of C++ requires that when using either kind of template (function or class), the implementation of that template must be visible at to the compiler at its use. The templates can still be kept in other files, but #include must be used to keep things visible.