Upload
michael-heron
View
65
Download
0
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
TEMPLATESMichael Heron
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.
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.
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);
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);
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.
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
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.
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;
}
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.
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;
}
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;
}
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;
}
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.
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.
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.
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;
}
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;
}
};
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;
}
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.
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.
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;
}
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.
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.
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);
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);
}
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.