CS253 Templates

This presentation was stolen from Chuck Anderson.

Initial Code

```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
```

Simplify

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.

Simplified

```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
```

Simplify Again

Can we do the same for the copy functions? Sure, why not?

Simplified Again

```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
```

Simplified more?

Do you see further simplifications? No, unless there is some way to parameterize the type of the arguments when you define the functions.

Templates to the rescue!

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

Classes

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

You saw this coming

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

Multiple template parameters

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

Integer template parameters

You can also include a constant integer to specify things like the size of an array, and can have a default value.

Example

```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
```

A less successful alternative

```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
4196528
0
4195904
0
167805536
32766
```

Range of template parameters

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

Range of template parameters

What can’t a template parameter be?

• a floating-point number
• a string
• an object

Remember, it’s a compile-time parameter. Types, integer constants, `true`, and `false`. That’s it!

Standard containers

• This is how the standard containers (the STL) are implemented.
• There’s nothing magic about them.
• `<vector>` contains, essentially:
```    template <typename T>
class vector {
…
};
```

What can be templated?

What can be templated?

• functions (including methods)
• classes
• variables (C++14)
• `using` aliases

User: Guest

Check: HTML CSS
Edit History Source

Modified: 2018-04-24T16:56

Apply to CSU | Contact CSU | Disclaimer | Equal Opportunity
Colorado State University, Fort Collins, CO 80523 USA