This presentation was stolen from Chuck Anderson.
double *copyDoubles(double *src, int num) { double *dest = new double[num]; for (int i=0; i<num; i++) dest[i] = src[i]; return dest; } char *copyLetters(char *src, int num) { char *dest = new char[num]; for (int i=0; i<num; i++) dest[i] = src[i]; return dest; } void printDoubles(double *src, int num) { for (int i=0; i<num; i++) cout << src[i] << '\n'; } void printLetters(char *src, int num) { for (int i=0; i<num; i++) cout << src[i] << '\n'; } int main() { double doubles[4] = {0.1, 0.2, 0.3, 0.4}; char letters[6] = {'a', 'b', 'c', 'd', 'e', 'f'}; double *doubles2 = copyDoubles(doubles,4); char *letters2 = copyLetters(letters,6); printDoubles(doubles2,4); printLetters(letters2,6); }
0.1 0.2 0.3 0.4 a b c d e f
Can this code be simplified? Yep. We don’t need both printDoubles
and printLetters
. Both can be named print
, because the compiler
can disambiguate the calls to print
by the types of the arguments.
double *copyDoubles(double *src, int num) { double *dest = new double[num]; for (int i=0; i<num; i++) dest[i] = src[i]; return dest; } char *copyLetters(char *src, int num) { char *dest = new char[num]; for (int i=0; i<num; i++) dest[i] = src[i]; return dest; } void print(double *src, int num) { for (int i=0; i<num; i++) cout << src[i] << '\n'; } void print(char *src, int num) { for (int i=0; i<num; i++) cout << src[i] << '\n'; } int main() { double doubles[4] = {0.1, 0.2, 0.3, 0.4}; char letters[6] = {'a', 'b', 'c', 'd', 'e', 'f'}; double *doubles2 = copyDoubles(doubles,4); char *letters2 = copyLetters(letters,6); print(doubles2,4); print(letters2,6); }
0.1 0.2 0.3 0.4 a b c d e f
Can we do the same for the copy functions? Sure, why not?
double *copy(double *src, int num) { double *dest = new double[num]; for (int i=0; i<num; i++) dest[i] = src[i]; return dest; } char *copy(char *src, int num) { char *dest = new char[num]; for (int i=0; i<num; i++) dest[i] = src[i]; return dest; } void print(double *src, int num) { for (int i=0; i<num; i++) cout << src[i] << '\n'; } void print(char *src, int num) { for (int i=0; i<num; i++) cout << src[i] << '\n'; } int main() { double doubles[4] = {0.1, 0.2, 0.3, 0.4}; char letters[6] = {'a', 'b', 'c', 'd', 'e', 'f'}; double *doubles2 = copy(doubles,4); char *letters2 = copy(letters,6); print(doubles2,4); print(letters2,6); }
0.1 0.2 0.3 0.4 a b c d e f
Do you see further simplifications? No, unless there is some way to parameterize the type of the arguments when you define the functions.
template <typename T> T *copy(T *src, int num) { T *dest = new T[num]; // could use auto for (int i=0; i<num; i++) dest[i] = src[i]; return dest; } template <typename T> // typename used to be class void print(T *src, int num) { for (int i=0; i<num; i++) cout << src[i] << '\n'; } int main() { double doubles[4] = {0.1, 0.2, 0.3, 0.4}; char letters[6] = {'a', 'b', 'c', 'd', 'e', 'f'}; double *doubles2 = copy(doubles,4); char *letters2 = copy(letters,6); print(doubles2,4); print(letters2,6); }
0.1 0.2 0.3 0.4 a b c d e f
See where T
was and was not used in copy()
.
Say we want to keep track of either integer or real 2-D points:
class PointInt { int x, y; public: PointInt(int xarg, int yarg) : x(xarg), y(yarg) { } int getx() const { return x; } int gety() const { return y; } }; class PointDouble { double x, y; public: PointDouble(double xarg, double yarg) : x(xarg), y(yarg) { } double getx() const { return x; } double gety() const { return y; } }; int main() { PointInt pi(1,2); PointDouble pd(3.4,2.5); cout << '(' << pi.getx() << ',' << pi.gety() << ")\n" << '(' << pd.getx() << ',' << pd.gety() << ")\n"; }
(1,2) (3.4,2.5)
Can we get rid of the duplications between PointInt
and
PointDouble
? Only if there is a way to parameterize the type of the
class.
template <typename T> class Point { T x, y; public: Point(T xarg, T yarg) : x(xarg), y(yarg) { } T getx() const { return x; } T gety() const { return y; } }; int main() { // This statement fails—must specify the type. // Point pi(1,2); Point<int> pi(1,2); Point<double> pd(3.4,2.5); cout << '(' << pi.getx() << ',' << pi.gety() << ")\n" << '(' << pd.getx() << ',' << pd.gety() << ")\n"; }
(1,2) (3.4,2.5)
You can have more than one parameter—just be sure you use the right type in the right place.
template <typename Tx, typename Ty> class Point { Tx x; Ty y; public: Point(Tx xarg, Ty yarg) : x(xarg), y(yarg) { } Tx getx() const { return x; } Ty gety() const { return y; } }; int main() { Point<int,int> pi(1,2); Point<double,double> pd(3.4,2.5); Point<int,double> pid(5, 6.42); cout << '(' << pi.getx() << ',' << pi.gety() << ")\n" << '(' << pd.getx() << ',' << pd.gety() << ")\n" << '(' << pid.getx() << ',' << pid.gety() << ")\n"; }
(1,2) (3.4,2.5) (5,6.42)
You can also include a constant integer to specify things like the size of an array, and can have a default value.
template <typename T, int N = 10> class Bunch { // Much like C++11 std::array T data[N]; public: void setData(int index, const T &item) { data[index] = item; } friend ostream &operator<< (ostream &out, const Bunch<T,N> &b) { for (int i=0; i<N; i++) out << b.data[i] << '\n'; return out; } }; int main() { Bunch<char,5> b; b.setData(0, 'a'); b.setData(1, 'b'); b.setData(2, 'c'); b.setData(3, 'd'); b.setData(4, 'e'); cout << b; }
a b c d e
template <typename T, int N = 10> class Bunch { T data[N]; public: void setData(int index, const T &item) { data[index] = item; } friend ostream &operator<< (ostream &out, const Bunch<T,N> &b) { for (int i=0; i<N; i++) out << b.data[i] << '\n'; return out; } }; int main() { Bunch<int> b; b.setData(0, 42); b.setData(1, 365); cout << b; }
42 365 0 0 4196560 0 4196032 0 1423153584 32767
What can a template parameter be?
typename T
class C
(the old way of saying typename
)
int N
bool B
template T
(not enough time in the semester)
Note that type doesn’t have to be a built-in boring
type like int
or float
. It can be a class
type,
or even filled-out templated type such as set<long>
.
What can’t a template parameter be?
Remember, it’s a compile-time parameter. Types, integer constants,
true
, and false
. That’s it!
<vector>
contains, essentially:
template <typename T> class vector { … };
What can be templated?
using
aliases