17. Generic Programming and Templates#
In Python, we could easily write functions that dealt with a variety of different input types without knowing those exact types as we wrote our code.
1//filename: script0.py
2//execute: python script0.py
3def min(a,b):
4 if (a < b):
5 return a
6 else:
7 return b
8
9print(min(5, 3))
10print(min(2.72, 3.14))
11print(min("world","hello"))
With C++’s static typing, we need to explicitly define those types.
The idea behind generic programming is to have templated functions and classes in which the logic is written independently of any particular type. As we use templated code, we then supply the appropriate types and the compiler instantiates functions and/or classes specific to the specified type(s). Effectively, these templates serve as blueprints and we still have the advantage of well-defined types.
17.1. Templated Functions#
In the following code, we define a templated function min that works for a variety of different types as long as that type (class) implements the < operator.
In this example, we declare a placeholder for the type name immediately before the function definition: template <typename t>
template<typename T>
returnType functionName(parameterList){
//function body
}
T can be any type. We can use other placeholder names than T and have multiple “substitution” types (just separate by commas)
If the compiler can determine the type(s) for the templated function, we do not need to explicitly state the type (line 16). However, in the case of line 17 as the two arguments differ in type, we need explicitly tell the compiler which type to using within the myMin function.
1//filename: min_template.cpp
2//complile: g++ -std=c++17 -o min_template min_template.cpp
3//execute: ./min_template
4#include <iostream>
5#include <string>
6using namespace std;
7
8template <typename T>
9T myMin(T a, T b) {
10 if (a < b) {
11 return a;
12 }
13 else {
14 return b;
15 }
16}
17
18int main(int argc, char *argv[]) {
19 cout << myMin(1.0,0.5) << endl;
20 cout << myMin<int>(42, 37.4) << endl;
21
22 // use the C++ string class
23 string s1("Jane");
24 string s2("John");
25 cout << myMin(s1, s2) << endl;
26
27 return EXIT_SUCCESS;
28}
17.2. Templated Classes#
Templated classes operate much the same way templated functions do. We specify the type substitutions ahead of the class definition instead of the function defintion.
template<typename T>
class ClassName {
private:
//private members
public:
//public members
}
As the entire class must be available for the compiler to instantiate a new class definition where the templated classes are instantiated, the entire body of the class is typically defined in header files.
1//filename: point.hpp
2//complile: g++ -std=c++17 -o point point.hpp
3//execute: ./point
4#include <string>
5#include <cmath>
6using std::string;
7
8#ifndef POINT_TEMPLATE_H_
9#define POINT_TEMPLATE_H_
10
11template <typename T>
12class Point {
13private:
14 T x;
15 T y;
16
17public:
18 Point(T initialX, T initialY) : x{initialX}, y{initialY} {}
19
20 T getX() const {
21 return x;
22 }
23
24 void setX(T val) {
25 x = val;
26 }
27
28 T getY() const{
29 return this->y;
30 }
31
32 void setY(T val) {
33 this->y = val;
34 }
35
36 T distance(Point other) const {
37 T dx = x - other.x;
38 T dy = y - other.y;
39 return sqrt(dx * dx + dy *dy);
40}
41
42 string toString() {
43 return "("+std::to_string(x)+","+std::to_string(y)+")";
44 }
45};
46
47#endif
The follow main method demonstrates using the templated Point class.
1//filename: testPoint.cpp
2//complile: g++ -std=c++17 -o testPoint testPoint.cpp
3//execute: ./testPoint
4#include "point.hpp"
5#include <iostream>
6#include <string>
7
8int main(int argc, char *argv[]) {
9 Point<double> dp(1.0,3.14);
10 std::cout << dp.toString() << std::endl;
11
12 Point<int> ip(1,5);
13 std::cout << ip.toString() << std::endl;
14
15 Point<char> cp('A','D');
16 std::cout << cp.toString() << std::endl;
17
18 Point<int> p(10,3);
19 std::cout << ip.distance(p) << std::endl;
20
21}
Notes:
Templated classes and functions are only type-checked once they are instantiated by the compiler. If the underlying type does not support operators or other function arguments than a compilation error will occur.
With functions, template parameters may be inferred. However, with classes, the template parameters must always be supplied. e.g.,
Point x(1,2)produces a compilation error.Note: instead of
template<typename T>,template<class T>can be used. The two statements have the same semantic meaning. While Bjarne Stroustrup states in Programming: Principles and Practices Using C++, “We are of the opinion that class already means type, so it makes no difference. Also, class is shorter.”, this statement leaves much to be desired. In C++, class actually means a user-defined type and is separate from the primitive types. Prefer the keywordtypenameto avoid confusion.