27
TEMPLATES Michael Heron

2CPP15 - Templates

Embed Size (px)

DESCRIPTION

This is an intermediate conversion course for C++, suitable for second year computing students who may have learned Java or another language in first year.

Citation preview

Page 1: 2CPP15 - Templates

TEMPLATESMichael Heron

Page 2: 2CPP15 - Templates

Introduction• In a previous lecture we talked very briefly about the idea

of parametric polymorphism.• Being able to treat specific types of object independent of what

they actually are.

• In this lecture we are going to talk about C++’s templating system.• This lets us incorporate parametric polymorphism correctly in our

programs.

Page 3: 2CPP15 - Templates

The Problem• Say we want to create a basic kind of data structure.

• A queue• A stack• A hashmap

• How do we do that and still be able to deal with object orientation?• Answer is not immediately straightforward.

Page 4: 2CPP15 - Templates

Java, Pre 1.5

• Many kinds of standard data structures exist as the standard libraries in Java.• Such as ArrayLists.

• Before version 1.5, the following syntax was used.• Requires explicit casting of objects as they come off of the structure.

• Why?• Polymorphism

Arraylist list = new ArrayList();

String str;

list.add (“Bing”);list.add (“Bong”);

str = (String)list.get(0);

Page 5: 2CPP15 - Templates

Java, 1.5+

• New versions of Java work with a different, more demonic syntax.• Explicit type information recorded along with the structure.• No need for casting as things come off of the structure.• How is such a thing achieved??

Arraylist<String> list = new ArrayList<String>();

String str;

list.add (“Bing”);list.add (“Bong”);

str = list.get(0);

Page 6: 2CPP15 - Templates

Generics/Templates• The system in Java and C# is known as the generics system.

• In C++, it’s called Templating.• Both systems are roughly equivalent.

• Used for the same purpose, with various technical differences.• Templates more powerful than the Java/C# implementation.

• Provides a method for type-casting structures to permit more elegant syntactic representation.

• Permits compile time checking of homogenous consistency.

Page 7: 2CPP15 - Templates

How Do They Work?• Code we provide serves as a template for C++ to

generate specific instances of the code.• Works like a structure that can deal with data at a family level of

granularity.

• Exists in two kinds:• Function templates• Class templates

Page 8: 2CPP15 - Templates

Function Templates

• Function templates mimic a more flexible form of method overloading.• Overloading requires a method to be implemented for

all possible types of data.

• Function templates allow us to resolve that down to a single template definition.

• Compiler can analyse provided parameters to assess the function that must be created.• Within limits… cannot interpret functionality where it

isn’t syntactically valid.

Page 9: 2CPP15 - Templates

Function Templates

template <class T>

T add_nums(T a, T b);

#include <iostream>

#include "TemplateTest.h"

using namespace std;

template <typename T>

T add_nums(T num1, T num2)

{

return num1+num2;

}

int main() {

cout << add_nums ('a', 1) << endl;

return 0;

}

Page 10: 2CPP15 - Templates

Ambiguity• Because it is the compiler doing the work of interpreting

parameters, it cannot deal well with ambiguity.• Where we say T, we must have it apply consistently across a

function call.

• We can ensure a certain kind of template being called by type-casting the function call.

Page 11: 2CPP15 - Templates

Ambiguous Function Call

#include <iostream>

#include "TemplateTest.h"

using namespace std;

template <typename T>

T add_nums(T num1, T num2)

{

return num1+num2;

}

int main() {

cout << add_nums (2.0f, 1) << endl;

return 0;

}

Page 12: 2CPP15 - Templates

Non-Ambiguous Function Call

#include <iostream>

#include "TemplateTest.h"

using namespace std;

template <typename T>

T add_nums(T num1, T num2)

{

return num1+num2;

}

int main() {

cout << add_nums<float> (2.0f, 1) << endl;

return 0;

}

Page 13: 2CPP15 - Templates

Multi-Type Functions

#include <iostream>

#include "TemplateTest.h"

using namespace std;

template <typename T, typename S> T add_nums(T num1, S num2)

{

return num1+num2;

}

int main() {

float f;

f = add_nums<float, int> (5.0f, 3);

cout << f << endl;

return 0;

}

Page 14: 2CPP15 - Templates

Class Templates

• In a similar way, we can create templates for C++ classes that are type agnostic.• Again, the compiler will infer typing from context where

it possibility can.

• Good design in C++ separates out definition and implementation.• Into .h and .cpp files.

• Slight problem with doing this the way we have done previously.• It won’t work.

Page 15: 2CPP15 - Templates

Class Templates

• Templates are not real code.• Not as we understand it.

• It’s like a template letter in a word processor.• Until the details are filled in, it’s not an actual letter.

• When separating out the definitions, the compiler can’t deal with the T until it knows what types it’s working with.• The code doesn’t exist until you use it.• This causes linking problems in the compiler.

Page 16: 2CPP15 - Templates

Class Template Problems• The complete definition for a function must be available at

the point of use.• If the full definition isn’t available, the compiler assumes it has been

defined elsewhere.

• In the main program, it knows the type of the data type we want to use.

• In the definition file, it doesn’t.• So it never generates the appropriate code.

• Solution – inline functions.

Page 17: 2CPP15 - Templates

A Stack Template

template <typename T>class Stack {

private:

T *stack;

int current_size;

public:

Stack() :

stack (new T[100]),

current_size (0) {

}

void push(T thing) {

if (current_size == 100) {

return;

}

stack[current_size] = thing;

current_size += 1;

}

Page 18: 2CPP15 - Templates

A Stack Template

T pop() {

T tmp;

current_size -= 1;

tmp = stack[current_size];

return tmp;

}

void clear() {

current_size = 0;

}

int query_size() {

return current_size;

}

~Stack() {

delete[] stack;

}

};

Page 19: 2CPP15 - Templates

Using The Stack

#include <iostream>

#include <String>

#include "Stack.h"

using namespace std;

int main() {

Stack<int> *myStack;

myStack = new Stack<int>();

myStack->push (100);

cout << myStack->pop() <<

endl;

return 0;

}

#include <iostream>

#include <String>

#include "Stack.h"

using namespace std;

int main() {

Stack<string> *myStack;

myStack = new Stack<string>();

myStack->push ("Hello");

cout << myStack->pop() <<

endl;

return 0;

}

Page 20: 2CPP15 - Templates

Benefits of Templates• Templates allow us to have classes and functions that

work independently of the data they use.• No need to write a hundred different stacks, just write one using a

template.

• No significant performance overhead.• It takes a little time for the compiler to generate the actionable

code, but it is not significant.

Page 21: 2CPP15 - Templates

Qualified and Unqualified Names

• What does the typename keyword mean in C++?• Have to go back quite a ways to explain this!

• Remember in our first header files, we’d often tell them to use a namspace?• To get access to the string class for one example.

• This is not good practice.• We tell every class that uses the header to make use of

that namespace.

Page 22: 2CPP15 - Templates

Qualified Names

• Qualified names are those that specifically note a scope in a reference.• For example, string is defined in std, thus:

• std::string • std::cout

• Scope is independent of using a namespace

#include <iostream>

int main() {

std::cout << "Bing!" << std::endl;

return 0;

}

Page 23: 2CPP15 - Templates

The Problem

template<class S>

void do_something()

{

S::x1 * x2;

}

What are we doing here?

Accessing a member variable called X1 in the parametrized class S?

Or…

Creating a pointer of type S::x1, with the name x2?

Typename resolves this problem.

Page 24: 2CPP15 - Templates

One Last Thing…• It’s often important to handle deep copies in templates.

• For the same reason it’s important in normal classes.

• Must include copy constructors and assignment operators here.• Remember the rules regarding these with reference to pointers and

others.

Page 25: 2CPP15 - Templates

Deep Copies on Templates

Stack<T>(const Stack<T> &s) {

current_size = s.current_size;

stack = new T[100];

for (int i = 0; i < 100; i++) {

stack[i] = s.stack[i];

}

}

Remember these are designed to work on value objects and must be explicitly de-referenced when working with pointers, like so:

Stack<string> *myStack, *stack2;myStack = new Stack<string>;stack2 = new Stack<string>(*myStack);

Page 26: 2CPP15 - Templates

Deep Copies on Templates

Stack<T>& operator= (const Stack<T> &s) {

current_size = s.current_size;

delete[] stack;

stack = new T[100];

for (int i = 0; i < 100; i++) {

stack[i] = s.stack[i];

}

return (*this);

}

Page 27: 2CPP15 - Templates

Summary• Method overloading offers a degree of ad hoc

polymorphism.• Combinations must be specified initially.

• Templates permit for parametric polymorphism.• More complex, but a powerful way of creating generic data types.

• We’ll see the power of this in the next lecture.