On Github mg6 / Templates
M. Gamrat & B. Kordalski
Let's have a function that returns a sum of 2 numbers.
Multiple type-specific implementations
float sum(float a, float b) { return a + b; }
int sum(int a, int b) { return a + b; }
char sum(char a, char b) { return a + b; }
The code is literally the same. How about we combine it?
template <class T> T sum(T a, T b) { return a + b; } // usage: int result1 = sum(4, -5); double result2 = sum(5.6, 2.4); char result3 = sum('.', ','); // equals 'Z'
Single declaration - 3 resultant functions - explain class keyword - type doesn't have to necessarily be of class type
template <class T> T sum(T a, T b) { return a + b; } sum(5.6, 2.4)
↓
5.6 → double 2.4 → double
↓
double sum(double a, double b) { return a + b; }
The compiler deduces proper type on function call.
important statement - compiler creates functions of only needed/used types
template <class T> /* templated function declaration here */
such as:
/* square(x) -> return x^2 */ template <class T> T square(T arg) { return arg * arg; }
syntax used also for classes
We can declare multiple type parameters as well:
template <class A, class B, class C> /* declaration here */
variable-length type lists also possible
(template<...> class list)
Type names A, B, C are arbitrary.
Obey variable naming conventions!
So far, so good. Now let's write a function returning greater value of 2 given.
#define to the rescue!
#define greater(x, y) ((x) > (y) ? (x) : (y)) // usage: float pi = 3.14f; float e = 2.71f; cout << greater(pi, e); // pi wins here
Wait for the answer
There is no type safety.
char* title = "hello there"; char* descr = "a description of a book"; greater(title, descr) // complete nonsense compiles easily!
Macro functions are just simple text replacements.
Wait for answer!
template <class T> T greater(T a, T b) { if (a > b) return a; return b; } // usage: int x1 = 5; int x2 = 3; cout << greater(x1, x2);
What if we had big objects to compare instead of int?
template <class T> T& greater(T& a, T& b) { if (a > b) return a; return b; } // since we return a reference now, // it's possible to do the following: int x1 = 5; int x2 = 3; greater(x1, x2) = -7; // which at runtime is an equivalent of: x1 = -7;
Now define a function that counts the sum of given array of elements.
Obviously we need the array x and its size n.
int sum_array(int data[], int count) { int sum = 0; for (int i = 0; i < count; ++i) sum += data[i]; return sum; }
template <class T> T sum_array(T data[], int count) { T sum = T(); // why not sum = 0? for (int i = 0; i < count; ++i) sum += data[i]; // watch out! return sum; } // usage: int int_array[4] = { 1, 6, 2, 1 }; char char_array[3] = { 'a', 'c', 'b' }; std::string text[2] = { "test", "ing" }; sum_array(int_array, 4) // == int(10) sum_array(char_array, 3) // == char('&') sum_array(text, 2) // == std::string("testing")
Explain differences - why T()? - assumption for operator+=
Let's have a 2D point structure.
struct Point { int x; int y; } // usage: Point p = {-2, 9};
Podstawy, proste, nic nowego
template <class T> struct Point { T x; T y; Point() : x(T()), y(T()) {} Point(T _x, T _y) : x(_x), y(_y) {} Point(const Point& p) : x(p.x), y(p.y) {} } // usage: Point<float> p(45.5f, 251.3f);
If we write:
Point<float> p;
we tell the compiler to replace all Ts in class template with float at compile time, so that the constructor can accept float parameters.
This is called the instantiation of class template.
That's the time the compiler checks for errors.
Are Point<float> and Point<unsigned> same classes?
No, they represent two different classes for the compiler.
Zaczekaj na odpowiedź z sali.
Point class would be useful. But we can do better.
Let's write a general purpose Pair class.
W ten sposób będziemy mieli klasę uniwersalną, która przechowa nie tylko punkty
template <class F, class S> struct Pair { F first; S second; Pair() : first( F() ), second( S() ) {} Pair(F _first, S _second) : first(_first), second(_second) {} Pair(const Pair& p) : first(p.first), second(p.second) {} } // usage: Pair<int, int> point(3, 5); Pair<std::string, float> product_rating; // or even: typedef Pair<float, float> Point; Point another(25.0f, -33.0f); // explicit values given Point yet_another(another); // copy from another
We can pass 2 different types to our class now.
Finally let's write a simple getter/setter class.
template <class T> class DataHolder { private: T data; public: DataHolder() : data( T() ) {} DataHolder(T value) : data(value) {} void set(T value) { data = value; } T get() { return data; } }
Wytłumacz set get
DataHolder<double> mass; mass.set(56.63); cout << mass.get() << endl;
Check out a great guide on C++ templates used for this presentation.
operator+, operator-