1 / 33

STAT 598W Lecture 16

STAT 598W Lecture 16. Templates. Templates. 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.

Download Presentation

STAT 598W Lecture 16

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. STAT 598W Lecture 16 Templates

  2. Templates • 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 <class T> 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 Three new functions are synthesized by the compiler, each following the “description” given in the template. 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

  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 <cstdlib>.   • Here is a template version.

  8. // bsearch.h template<class T1, class T2> // 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; } 1 if s > t cmp(s,t) = 0 if s = t -1 if s < t Try this with [ 1, 4, 8, 14, 22, 23, 30 ] and search for 4

  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<int>, the compiler must see both the Foo template and the fact that you're trying to make a specific Foo<int>. • 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<typename T> extern void foo(); // File "foo.cpp" #include <iostream> #include "foo.h" template<typename T> void foo() { std::cout << "Here I am!\n"; } template void foo<int>(); // File "main.cpp" #include "foo.h" int main() { foo<int>(); ... }

  13. A Class for Names and Ages //NameAge.h #include <string> 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 T1 is NameAge T2 is string Ed Fred Ned Ted 40 30 76 24 namesAndAges[0] namesAndAges[3] Suppose these are already ordered by name. We want to find Fred’s age.

  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 <iostream> #include <string> 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 <ostream> using std::ostream; template<class T, int n> class Vector { public: Vector() { } // default constructor Vector(T v0); // init elements to v0 Vector<T, n> operator+(const Vector<T,n> & v) const; T & operator[](int i); friend ostream & operator<<(ostream &, const Vector<T,n> &); private: Tcontents[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<double,3> dv1(1.2), dv2(2.5); Vector<double,3> 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 <class T, int n> Vector<T,n>::Vector(T v0) { for (int i = 0; i < n; i++) contents[i] = v0; } template <class T, int n> Vector<T,n> Vector<T,n>::operator+(const Vector<T,n> a) const { Vector<T,n> ans; for (int i = 0; i < n; i++) ans.contents[i] = a.contents[i] + contents[i]; return ans; }

  23. More Definitions template <class T, int n> T & Vector<T,n>::operator[](int i) { assert(i >= 0 && i < n) return contents[i]; } template <class T, int n> ostream & operator<<(ostream & out, const Vector<T,n> & 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<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class Vector<double,3> 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 T, int n> class Vector { public: ... friend ostream & operator<<(ostream & out, const Vector<T,n> & 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<T,n>::contents” is inaccessible

  27. The “Container Class Problem” Dangerous Bend! • 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:

  28. More Advanced Stuff

  29. Stack Holding Pointers // TStack.h template<class T> 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<string> 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?

More Related