Download presentation
Presentation is loading. Please wait.
Published byAnna Parks Modified over 9 years ago
1
STAT 598W Lecture 16 Templates
2
Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome of C++'s strong typing requirements. But as always, the strong typing is there somewhere, behind the scenes.
3
Function Templates For a template function, declare that one or more argument types are arbitrary, to be filled in when it's time to use the function. Instead of specifying the types of the formal parameters, we give a kind of “variable” for the types.
4
Function Template Example template T power (T a, int exp) { T ans = a; while (--exp > 0 ) ans = ans * a; return ans; } T can be any type known to the compiler. Note the assumption that operator* and operator= have been defined for the type that is used.
5
Function Template Example void main() { int i = 5, j = 2; float r = 0.5; double d = 12.345; cout << power(j, i) << endl; cout << power(r, i) << endl; cout << fixed << setprecision(3) << power(d, i) << endl; } 32 0.03125 286718.339 Three new functions are synthesized by the compiler, each following the “description” given in the template.
6
Function Templates It is the compiler that creates new functions, given the template the programmer provides. One copy is made for each required signature. Type checking still takes place.
7
A More Ambitious Example Binary search through an ordered list is the same no matter what type the list contains. Why not write the search routine once and for all? The C library has a “void pointer” version in. Here is a template version.
8
// bsearch.h template // template header int bsearch(T1 arr[],// ordered array of any type T1 T2 key,// search key of any type T2 int low, int high,// inclusive search range int (* cmp)(T1, T2)) {// comparison function int mid, test; while (low <= high) { mid = (low + high)/2; test = cmp(arr[mid], key); if (test > 0) high = mid - 1; else if (test < 0) low = mid + 1; else return mid; } return -1; } Try this with [ 1, 4, 8, 14, 22, 23, 30 ] and search for 4 1 if s > t cmp(s,t) = 0 if s = t -1 if s < t
9
bsearch Notice that the template function takes as an argument a pointer to the comparison function. This gives us freedom to search arrays of any type, native or user-defined, so long as a comparison function is provided. Don’t use separate declaration and definition files, since at the spot where bsearch is called, the compiler needs to know everything about the function. Our text isn’t very clear about this... Try the programming practice Separating Template Declarations and Definitions.docx
10
Template definitions and declarations linking issue A template is not a class or a function. A template is a "pattern" that the compiler uses to generate a family of classes or functions. In order for the compiler to generate the code, it must see both the template definition (not just declaration) and the specific types/whatever used to "fill in" the template. For example, if you're trying to use a Foo, the compiler must see both the Foo template and the fact that you're trying to make a specific Foo. Your compiler probably doesn't remember the details of one.cpp file while it is compiling another.cpp file. It could, but most do not. BTW this is called the "separate compilation model."
11
Solutions to linking issue First choice: put definitions and declarations together into one header file. Second choice: If you have to use a separate.cpp file for the definitions, then add one line declaration of the specific type you will use into the cpp file.
12
Example // File "foo.h" template extern void foo(); // File "foo.cpp" #include #include "foo.h" template void foo() { std::cout << "Here I am!\n"; } template void foo (); // File "main.cpp" #include "foo.h" int main() { foo ();... }
13
A Class for Names and Ages //NameAge.h #include using std::string; class NameAge { string name; int age; public: NameAge(string n, int a) : name(n), age(a) {} string getName(); int getAge(); }; int compareOnName(NameAge, string);
14
An Array of NameAges Ed Fred Ned Ted 40 30 76 24 Suppose these are already ordered by name. We want to find Fred’s age. namesAndAges[0] namesAndAges[3] T1 is NameAge T2 is string
15
NameAge Definitions, and the Comparison Function // NameAge.cpp #include "NameAge.h" string NameAge::getName() { return name; } int NameAge::getAge() { return age; } int compareOnName(NameAge na, string n) { if (na.getName() > n) return 1; else if (na.getName() < n) return -1; else return 0; } Note that > is predefined for the string class. It implements lexicographic order.
16
Using bsearch #include "NameAge.h" #include "bsearch.h" #include using namespace std; NameAge namesAndAges[] = { NameAge("Ed", 40), NameAge("Fred", 30), NameAge("Ned", 76), NameAge("Ted", 24) }; void main() { int LEN = sizeof(namesAndAges)/sizeof(NameAge); // array size string name(“Fred"); int i = bsearch(namesAndAges, name, 0, LEN-1, compareOnName); if (i >= 0) cout << namesAndAges[i].getAge() << endl; }
17
Another Template Factoid We may also write non-template (“concrete”) versions of the same function. The compiler first looks for a concrete function of a given name and signature, and if none is found, tries to build one from the template.
18
Class Templates Consider our trusty Vector class. It holds doubles. How to extend it? Copy & paste? Do something with void pointers? The Java approach: since everything derives from the top-level class Object, make a Vector of Objects. Then polymorphism comes to the rescue. But C++ doesn't insist that there be a single inheritance tree. Use C++ templates (first done as macros, then added to the language).
19
Yet Another Vector Class // Vector.h // n-dimensional vector of type T elements #include using std::ostream; template class Vector { public: Vector() { }// default constructor Vector(T v0);// init elements to v0 Vector operator+(const Vector & v) const; T & operator[](int i); friend ostream & operator &); private: T contents[n];// internal array of type T };
20
Using the Vector Template T is the substitution parameter, representing a type name The integer n is not explicitly part of the class, it's a compile-time constant for the class With this header (and assuming the implementation), we can declare void main() { Vector dv1(1.2), dv2(2.5); Vector dv3 = dv1 + dv2; cout << dv3 << endl; }
21
Some Nagging Questions Where are assignment and the copy constructor? And the destructor??? They aren’t needed: the internal array is not on the heap. Why don’t we need a member variable holding the length? There are separate Vector classes for each length.
22
The Definitions template Vector ::Vector(T v0) { for (int i = 0; i < n; i++) contents[i] = v0; } template Vector Vector ::operator+(const Vector a) const { Vector ans; for (int i = 0; i < n; i++) ans.contents[i] = a.contents[i] + contents[i]; return ans; }
23
More Definitions template T & Vector ::operator[](int i) { assert(i >= 0 && i < n) return contents[i]; } template ostream & operator & v) { out << "[ " ; for (int i = 0; i < n-1; i++) out << v.contents[i] << ", " ; out << v.contents[n-1] << " ]" ; return out; }
24
Wierd Microsoft “Gotchas” The code as shown compiles nicely, but the linker gives this: 1>TestTemplateVector.obj : error LNK2001: unresolved external symbol "class std::basic_ostream<char,struct std::char_traits > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits > &,class Vector const &)" suggesting that the << operator couldn’t be instantiated properly. Apparently the operator needs to be defined within the class declaration. OK.
25
Wierd Microsoft “Gotchas” So, define the method within the class declaration: template class Vector { public:... friend ostream & operator<<(ostream & out, const Vector & v) { out << "[ " ; for (int i = 0; i < n-1; i++) out << v.contents[i] << ", " ; out << v.contents[n-1] << " ]" ; return out; }... };
26
Wierd Microsoft “Gotchas” This compiles and links (and runs!) properly. The only problem is... “Mr. Intellisense” doesn’t like it... Error: member “Vector ::contents” is inaccessible
27
The “Container Class Problem” Does the container hold objects, or pointers to objects? Unless we hold pointers (or references), we’re stuck with homogeneous types. But with pointers, there is the memory management problem: who owns the actual objects? Here is a stack, holding pointers: Dangerous Bend!
28
More Advanced Stuff
29
Stack Holding Pointers // TStack.h template class Stack{ struct Link { T * data; Link * next; Link(T * dat, Link * nxt) : data(dat), next(nxt) {} } * head;
30
Stack Holding Pointers public: Stack() : head(0) {} ~Stack() { while(head) delete pop(); } void push(T * dat) { head = new Link(dat, head); } T * peek() const { return head ? head->data : 0; }
31
Stack Holding Pointers T * pop() { if (head == 0) return 0; T * result = head->data; Link * oldHead = head; head = head->next; delete oldHead; return result; } };
32
A Stack of strings Void main() { Stack textlines; textlines.push(new string(“Xue”)); textlines.push(new string(“Yue”)); textlines.push(new string(“Sue”)); // do stuff with the lines }
33
Memory Issues But what if the things being pointed to are also pointed to by something else? For example, the strings above might be held in several stacks, in various orders. Don't forget cleanup…who is in charge?
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.