Show Lecture.Templates as a slide show.
This presentation was stolen from Prof. Chuck Anderson.
double *copyDoubles(const double *src, int num) { double *dest = new double[num]; for (int i=0; i<num; i++) dest[i] = src[i]; return dest; } char *copyLetters(const char *src, int num) { char *dest = new char[num]; for (int i=0; i<num; i++) dest[i] = src[i]; return dest; } void printDoubles(const double *src, int num) { for (int i=0; i<num; i++) cout << src[i] << '\n'; } void printLetters(const char *src, int num) { for (int i=0; i<num; i++) cout << src[i] << '\n'; } int main() { double doubles[] = {0.1, 0.2, 0.3, 0.4}; double *doubles2 = copyDoubles(doubles, 4); char *letters2 = copyLetters("bonehead", 8); printDoubles(doubles2, 4); printLetters(letters2, 8); }
0.1 0.2 0.3 0.4 b o n e h e a d
printDoubles
and printLetters
.
print
, because the compiler can disambiguate the
calls to print
by the types of the arguments.
double *copyDoubles(const double *src, int num) { double *dest = new double[num]; for (int i=0; i<num; i++) dest[i] = src[i]; return dest; } char *copyLetters(const char *src, int num) { char *dest = new char[num]; for (int i=0; i<num; i++) dest[i] = src[i]; return dest; } void print(const double *src, int num) { for (int i=0; i<num; i++) cout << src[i] << '\n'; } void print(const char *src, int num) { for (int i=0; i<num; i++) cout << src[i] << '\n'; } int main() { double doubles[] = {0.1, 0.2, 0.3, 0.4}; double *doubles2 = copyDoubles(doubles, 4); char *letters2 = copyLetters("bonehead", 8); print(doubles2, 4); print(letters2, 8); }
0.1 0.2 0.3 0.4 b o n e h e a d
double *copy(const double *src, int num) { double *dest = new double[num]; for (int i=0; i<num; i++) dest[i] = src[i]; return dest; } char *copy(const char *src, int num) { char *dest = new char[num]; for (int i=0; i<num; i++) dest[i] = src[i]; return dest; } void print(const double *src, int num) { for (int i=0; i<num; i++) cout << src[i] << '\n'; } void print(const char *src, int num) { for (int i=0; i<num; i++) cout << src[i] << '\n'; } int main() { double doubles[] = {0.1, 0.2, 0.3, 0.4}; double *doubles2 = copy(doubles, 4); char *letters2 = copy("bonehead", 8); print(doubles2, 4); print(letters2, 8); }
0.1 0.2 0.3 0.4 b o n e h e a d
template <typename T> T *copy(const 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 <class T> // old-fashioned syntax void print(T *src, int num) { for (int i=0; i<num; i++) cout << src[i] << '\n'; } int main() { double doubles[] = {0.1, 0.2, 0.3, 0.4}; double *doubles2 = copy(doubles, 4); char *letters2 = copy("bonehead", 8); print(doubles2, 4); print(letters2, 8); }
0.1 0.2 0.3 0.4 b o n e h e a d
T
is a placeholder, a compile-time argument representing a type.
template <typename T> T *copy(const 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 <class T> // old-fashioned syntax void print(T *src, int num) { for (int i=0; i<num; i++) cout << src[i] << '\n'; } int main() { double doubles[] = {0.1, 0.2, 0.3, 0.4}; double *doubles2 = copy(doubles, 4); char *letters2 = copy("bonehead", 8); print(doubles2, 4); print(letters2, 8); }
0.1 0.2 0.3 0.4 b o n e h e a d
T
,
for type.
U
,
because U
comes after T
.
T1
& T2
are also common.
template <typename T> T *copy(const 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 <class T> // old-fashioned syntax void print(T *src, int num) { for (int i=0; i<num; i++) cout << src[i] << '\n'; } int main() { double doubles[] = {0.1, 0.2, 0.3, 0.4}; double *doubles2 = copy(doubles, 4); char *letters2 = copy("bonehead", 8); print(doubles2, 4); print(letters2, 8); }
0.1 0.2 0.3 0.4 b o n e h e a d
template <class T>
is old‑fashioned.
template <typename T>
is newer.
class
types such as int
.
template <typename T> T *copy(const 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 <class T> // old-fashioned syntax void print(T *src, int num) { for (int i=0; i<num; i++) cout << src[i] << '\n'; } int main() { double doubles[] = {0.1, 0.2, 0.3, 0.4}; double *doubles2 = copy(doubles, 4); char *letters2 = copy("bonehead", 8); print(doubles2, 4); print(letters2, 8); }
0.1 0.2 0.3 0.4 b o n e h e a d
T
in copy
is completely separate
from the use of T
in print
.
template <typename T> T *copy(const 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 <class T> // old-fashioned syntax void print(T *src, int num) { for (int i=0; i<num; i++) cout << src[i] << '\n'; } int main() { double doubles[] = {0.1, 0.2, 0.3, 0.4}; double *doubles2 = copy(doubles, 4); char *letters2 = copy("bonehead", 8); print(doubles2, 4); print(letters2, 8); }
0.1 0.2 0.3 0.4 b o n e h e a d
copy
has a const
in the arguments.
print
does not.
T
represent in each?
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)
PointInt
and
PointDouble
?
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 = 5> class Handful { // Much like std::array T data[N]; public: void setData(int index, const T &item) { data[index] = item; } friend ostream &operator<< (ostream &out, const Handful<T, N> &h) { for (int i=0; i<N; i++) out << h.data[i] << '\n'; return out; } }; int main() { Handful<char, 3> h; h.setData(0, 'a'); h.setData(1, 'b'); h.setData(2, 'c'); cout << h; }
a b c
operator<<
a member function? It’s in the class
!
template <typename T, int N = 5> class Handful { T data[N]; public: void setData(int index, const T &item) { data[index] = item; } friend ostream &operator<< (ostream &out, const Handful<T, N> &h) { for (int i=0; i<N; i++) out << h.data[i] << '\n'; return out; } }; int main() { Handful<int> h; h.setData(0, 42); h.setData(1, 365); cout << h; }
42 365 4196032 0 -878455120
What can a template parameter be?
typename T
or class C
(how quaint!)
int N
, short N
, long N
, bool B
, char C
, …
int *IP
, string *SP
, …
template T
(not enough time in the semester)
A 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, integral values, and pointers.
<vector>
contains, essentially:
template <typename T> class vector { … };
using
declarationsA using
declaration can be templated to create a new type:
template<class T> using Counter = map<T, size_t>; int main() { Counter<string> c; c["alpha"]++; c["beta"]++; c["alpha"]++; for (auto v : c) cout << v.first << ' ' << v.second << '\n'; }
alpha 2 beta 1
God help us, even variables can be templated:
template <typename T> constexpr T pi = T(3.14159265358979323846264); template <> const string pi<string> = "π"; int main() { cout << pi<long double> << '\n'; cout << pi<int> << '\n'; cout << pi<string> << '\n'; }
3.14159 3 π
What can be templated?
using
aliases