STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class...

33
STAT 598W Lecture 16 Templates

Transcript of STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class...

Page 1: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

STAT 598W Lecture 16

Templates

Page 2: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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.

Page 3: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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.

Page 4: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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 tothe compiler.

Note the assumption thatoperator* and operator=have been defined for thetype that is used.

Page 5: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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 aresynthesized by thecompiler, each followingthe “description” givenin the template.

Page 6: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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.

Page 7: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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.

Page 8: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

// bsearch.htemplate<class T1, class T2> // template headerint 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 > tcmp(s,t) = 0 if s = t -1 if s < t

Page 9: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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

Page 10: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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

Page 11: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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.

Page 12: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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>(); ... }

Page 13: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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

Page 14: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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 NameAgeT2 is string

Page 15: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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 predefinedfor the string class. Itimplements lexicographicorder.

Page 16: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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

Page 17: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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.

Page 18: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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

Page 19: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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: T contents[n]; // internal array of type T };

Page 20: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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

Page 21: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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.

Page 22: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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

Page 23: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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

Page 24: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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 withinthe class declaration. OK.

Page 25: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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; } ...};

Page 26: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

Wierd Microsoft “Gotchas”

This compiles and links (and runs!) properly. The only problemis... “Mr. Intellisense” doesn’t like it...

Error: member “Vector<T,n>::contents” is inaccessible

Page 27: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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!

Page 28: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

More Advanced Stuff

Page 29: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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;

Page 30: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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

Page 31: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

Stack Holding Pointers

T * pop() { if (head == 0) return 0; T * result = head->data; Link * oldHead = head; head = head->next; delete oldHead; return result; } };

Page 32: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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 }

Page 33: STAT 598W Lecture 16 Templates. Templates can be used in function definitions and in class definitions. Roughly, templates are a way to (apparently) overcome.

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?