* Can you write a program to read integers from the input and print them in sorted order? #include #include #include int main() { std::vector data; int val = 0; while (std::cin >> val) { data.push_back(val); } std::sort(data.begin(), data.end()); for (int elem: data) { std::cout << elem << " "; } std::cout << std::endl; } * What is this syntax "vector"? * What if we want to sort strings? #include #include #include int main() { std::vector data; std::string val; while (std::cin >> val) { data.push_back(val); } std::sort(data.begin(), data.end()); for (std::string elem: data) { std::cout << elem << " "; } std::cout << std::endl; } * What are the differences between these two programs? * Are they redundant? * How could we factor out this redundancy? #include #include #include template void readNSort() { std::vector data; T val; while (std::cin >> val) { data.push_back(val); } std::sort(data.begin(), data.end()); for (T elem: data) { std::cout << elem << " "; } std::cout << std::endl; } int main() { readNSort(); } * What does the declaration 'template void readNSort()' indicate? * What if we wanted to sort strings? #include #include #include template void readNSort() { std::vector data; T val; while (std::cin >> val) { data.push_back(val); } std::sort(data.begin(), data.end()); for (T elem: data) { std::cout << elem << " "; } std::cout << std::endl; } int main() { readNSort(); } * Can you test it with doubles? int main() { readNSort(); } * Does it work for any type? - Not really! * What does a type must have to work with readNSort? - The in-streaming operator >> - The out-streaming operator << - Must be "sortable", i.e.: has the greater than operator > * Is it the case that any type has these operators? Consider: struct S { int x, y; }; int main() { readNSort(); } * Let's sum up the elements, instead of sorting them: #include #include template T readNSum(std::deque &data) { T sum = data.front(); data.pop_front(); for (T elem: data) { sum = sum + elem; } return sum; } int main() { std::deque data; data.push_back(1); data.push_back(2); data.push_back(3); std::cout << readNSum(data) << std::endl; } * What's a deque? - That's a vector from where elements can be removed at both ends. * Does the program above work with doubles? int main() { std::deque data; data.push_back(1.1); data.push_back(2.2); data.push_back(3.3); std::cout << readNSum(data) << std::endl; } * Strings? int main() { std::deque data; data.push_back("a"); data.push_back("a"); data.push_back("a"); std::cout << readNSum(data) << std::endl; } * Does it work with struct S, i.e.: struct S { int x, y; }; int main() { struct S s; s.x = 1; s.y = 2; std::deque data; data.push_back(s); data.push_back(s); data.push_back(s); std::cout << readNSum(data).x << std::endl; } * What does it have to do to work with "struct S"? struct S { int x, y; S operator + (const S &obj) { S res; res.x = obj.x + x; res.y = obj.y + y; return res; } }; template T readNSum(std::deque &data) { T sum = data.front(); data.pop_front(); for (T elem: data) { sum = sum + elem; } return sum; } * Would the code above work if I changed the implementation of readNSum like the code below? template T readNSum(std::deque &data) { T sum = data.front(); data.pop_front(); for (T elem: data) { sum += elem; } return sum; } * What we need to do to ensure that it compiles? struct S { int x, y; S operator + (const S &obj) { S res; res.x = obj.x + x; res.y = obj.y + y; return res; } S& operator += (const S &obj) { x = obj.x + x; y = obj.y + y; return *this; } }; * Can we use two types in the same generic declaration? #include template class Pair { T first; U second; public: Pair(T f, U s): first(f), second(s) {} T getFirst() { return first; } U getSecond() { return second; } }; int main() { Pair p0(1, "um"); std::cout << p0.getFirst() << std::endl; std::cout << p0.getSecond() << std::endl; Pair p1(3.14, true); std::cout << p1.getFirst() << std::endl; std::cout << p1.getSecond() << std::endl; } * Consider the template below. Can we remove the type declaration from "k=GetMax(i, j)", for instance? #include using namespace std; template T GetMax (T a, T b) { return (a>b?a:b); } int main () { int i=5, j=6, k; long l=10, m=5, n; k=GetMax(i,j); n=GetMax(l,m); cout << k << endl; cout << n << endl; cout << GetMax("bola", "ovo"); return 0; } * And how is this template implemented in assembly? $> clang++ -S GetMax.cpp ... __Z6GetMaxIiET_S0_S0_ ... __Z6GetMaxIlET_S0_S0_ ... __Z6GetMaxINSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEEET_S7_S7_ * How to declare a generic class member outside the class definition? template class Pair { T first; U second; public: Pair(T f, U s): first(f), second(s) {} T getFirst(); U getSecond() { return second; } }; template T Pair::getFirst() { return first; } * Is the program below going to compile? #include using namespace std; struct Interval { int x, y; }; template T GetMax (T a, T b) { return (a>b?a:b); } int main () { Interval i0, i1; i0.x = 1; i0.y = 10; i1.x = 2; i1.y = 4; Interval i2 = GetMax(i0,i1); cout << i2.x << endl; return 0; } * How to fix it? #include using namespace std; struct Interval { int x, y; bool operator> (const Interval& i1) { return x < i1.x && y > i1.y; } }; template T GetMax (T a, T b) { return (a>b?a:b); } int main () { Interval i0, i1; i0.x = 1; i0.y = 10; i1.x = 2; i1.y = 4; Interval i2 = GetMax(i0,i1); cout << i2.x << endl; return 0; } * Do you think this makes sense? int main () { Interval i0, i1; i0.x = 1; i0.y = 8; i1.x = 6; i1.y = 14; Interval i2 = GetMax(i0,i1); cout << i2.x << endl; return 0; } // The output is 6 * Can you change the behavior of GetMax, but only for Interval? #include using namespace std; struct Interval { int x, y; }; template T GetMax (T a, T b) { return (a>b?a:b); } template <> Interval GetMax (Interval a, Interval b) { Interval i; i.x = a.x > b.x ? a.x : b.x; i.y = a.y > b.y ? a.y : b.y; return i; } int main () { Interval i0, i1; i0.x = 1; i0.y = 10; i1.x = 2; i1.y = 4; Interval i2 = GetMax(i0,i1); cout << i2.x << endl; return 0; } * Take a look into the class below. Are all the generic elements "Types"? // sequence template #include using namespace std; template class BoundedArray { T memblock [N]; public: void set (int x, T value); T get (int x); }; template void BoundedArray::set (int x, T value) { memblock[x]=value; } template T BoundedArray::get (int x) { return memblock[x]; } int main () { BoundedArray myints; BoundedArray myfloats; myints.set (0,100); myfloats.set (3,3.1416); cout << myints.get(0) << '\n'; cout << myfloats.get(3) << '\n'; return 0; } * Can you modify the class above to avoid accessing the array outside bounds? // sequence template #include #include using namespace std; template class BoundedArray { T memblock [N]; public: void set (int x, T value); T get (int x); }; template void BoundedArray::set (int x, T value) { assert(x >= 0 && x < N); memblock[x]=value; } template T BoundedArray::get (int x) { assert(x >= 0 && x < N); return memblock[x]; } * What happens if I try to access an invalid bound? int main () { BoundedArray myfloats; myfloats.set (3,3.1416); cout << myfloats.get(6) << '\n'; return 0; } * Can we write a program like this? int main (int argc, char** argv) { BoundedArray myints; myints.set (0,100); cout << myints.get(0) << '\n'; return 0; } * What is the problem with the program above? * Can we separate a template into header and implementation modules? // BoundedArray.h: #ifndef BOUNDED_ARRAY_H #define BOUNDED_ARRAY_H template class BoundedArray { T memblock [N]; public: void set (int x, T value); T get (int x); }; #endif // BoundedArrayMain.cpp: #include #include #include "BoundedArray.h" template void BoundedArray::set (int x, T value) { assert(x >= 0 && x < N); memblock[x]=value; } template T BoundedArray::get (int x) { assert(x >= 0 && x < N); return memblock[x]; } #include // BoundedArrayMain.cpp: using namespace std; #include "BoundedArray.h" int main () { BoundedArray myints; BoundedArray myfloats; myints.set (0,100); myfloats.set (3,3.1416); cout << myints.get(0) << '\n'; cout << myfloats.get(6) << '\n'; return 0; } * Why the program above will not compile? * Can you still separate the header file? // BoundedArray.h: #ifndef BOUNDED_ARRAY_H #define BOUNDED_ARRAY_H template class BoundedArray { T memblock [N]; public: void set (int x, T value); T get (int x); }; #endif // BoundedArray3.cpp #include #include #include "BoundedArray.h" using namespace std; template void BoundedArray::set (int x, T value) { assert(x >= 0 && x < N); memblock[x]=value; } template T BoundedArray::get (int x) { assert(x >= 0 && x < N); return memblock[x]; } int main () { BoundedArray myints; BoundedArray myfloats; myints.set (0,100); myfloats.set (3,3.1416); cout << myints.get(0) << '\n'; cout << myfloats.get(6) << '\n'; return 0; } * Why is this type of polymorphism called "Parametric Polimorphism"? // The type constructor: template