Download presentation
Presentation is loading. Please wait.
1
1 Parametric Polymorphism Polymorphism Techniques C++ Templates
2
2 Polymorphism: Overloading void f(int n) { cout << n << endl; } struct Num { Num(int v = 0) : value(v) { } int value; }; void f(Num n) { cout << n.value << endl; } int main(int, char**) { Num n(5); f(5); f(n); return 0; }
3
3 void f(int n) { cout << n << endl; } struct Num { Num(int v = 0) : value(v) { } int value; operator int() { return value; } }; int main(int, char**) { Num n(5); f(5); f(n); return 0; } Polymorhism: Coercion
4
4 struct Num { Num(int v = 0) : value(v) { } int value; }; struct Num2 : Num { Num2(int v = 0) : Num(v) { } void add(int a) { value += a; } }; void f(Num& n) { cout << n.value << endl; } int main(int, char**) { Num x(5); Num2 y(5); f(x); f(y); return 0; } Polymorhism: Inclusion (Subtyping)
5
5 struct Num { Num(int v = 0) : value(v) { } int value; }; struct Person { Person(const char* name) : value(name) { } const char* value; }; template void f(T& t) { cout << t.value << endl; } int main(int, char**) { Num x(5); Person y("Christopher Nolan"); f(x); f(y); return 0; } Polymorphism: Parametric
6
6 Taxonomy of Polymorphism Ad-hoc Overloading Coercion Universal Inclusion (subtyping) Parametric
7
7 Parametric Polymorphism Allows definitions to be parametrized at compile-time Such a definition is actually a “function” that returns a new program element(method, class, etc.) Template parameters = The arguments of this “function” AKA: type parameters Instantiation of a template == evaluating the “function”
8
8 Template Instantiation in C++ The compiler recompiles the template definition Substitutes the formal template parameters with the actual parameters Generates new object code Almost no compilation when the definition is read Most compilation work is at each instantiation point The template definition must be part of the source code That is: #included from an.h file Supporting separate compilation of templates is too difficult in practice
9
9 C++ Function Templates A function can have type parameters template T* create() { return new T(); } void f() { string* sp = create (); } we use create() thru explicit instantiation We specify the actual types inside a pair of In some cases the compiler can deduce the template parameters template void print(T& t) { cout << t << endl; } void g() { print(5); } We use print() thru implicit instantiation Possible if a type parameter is also a value parameter Make overload resolution more difficult Introduced later
10
10 A Template Taking a Constant template struct FixedArray { double values[N]; int size() { return N; } // Could be static }; int main(int, char**) { FixedArray arr; arr.values[0] = 3.14; cout << "first element=" << arr.values[0] << endl; cout << "size=" << arr.size() << endl; return 0; }
11
11 A Template Taking a Function Pointer template struct Caller { static void run() { F(); } }; void hi() { cout << "hi" << endl;} void bye() { cout << "bye" << endl; } int main(int, char**) { Caller ::run(); return 0; }
12
12 A Template Taking a Type template struct Pair { void set(const T& x, const T& y) { a = x; b = y; } void print() { cout << a << "," << b << endl; } private: T a, b; }; typedef Pair StrPair; // Instantiate Pair, // pass char* as an argument typedef Pair IntPair; int main(int, char**) { StrPair sp; IntPair ip; sp.set("ab", "cd"); sp.print(); ip.set(10, 20); ip.print(); return 0; }
13
13Specialization template struct Pair { void set(const T& x, const T& y) { a = x; b = y; } void print() { cout << a << "," << b << endl; } private: T a, b; }; template<> struct Pair { void set(bool x, bool y) { v = (x?1:0) + (y?2:0); } void print() { cout << (v&1) << "," << (v&2) << endl; } private: int v; }; int main(int, char**) { Pair pb; pb.set(true, false); pb.print(); Pair pc; pc.set('x', 'y'); pc.print(); return 0; }
14
14 A Non Conforming Specialization is Legal template struct Pair { void set(const T& x, const T& y) { a = x; b = y; } void print() { cout << a << "," << b << endl; } private: T a, b; }; template<> struct Pair { void set(bool x, bool y) { v = (x?1:0) + (y?2:0); } public: int v; }; void doSomething() { Pair pb; pb.set(true, false); cout << pb.v << endl; Pair pc; pc.set('x', 'y'); pc.print(); pb.print(); // Error. Pair ::print() is undefined }
15
15 C++ Templates: Interim Summary A template is a “function” Arguments: types, constants Return value : A new class or a new function Recognized by the compiler The template is recompiled with each Instantiation Specialization == “if” Different result based on the actual arguments
16
16 C++ Templates as a Programming Languages Values? Fundamental computational step? What is the run-time phase? What is the compile-time phase? Type system: static or dynamic? How powerful is it? What programs can we write in this language? We saw that we can do “if” decisions What sorts of bugs does it have? Can we run into an infinite loop?
17
17 A Non-Terminating Compilation template struct Loop { typedef typename Loop >::Temp Temp; }; int main(int, char**) { Loop n; return 0; }
18
18Concepts Concept = A set of requirements that a type (or a set of types) should uphold Requirements == constraints == expectations A generalization of structural conformance A C++ template defines a concept Expects its type parameter to support certain operations But the concept is implict The requirements are not clearly defined They are hidden inside the code
19
19 Modelling the “From” Concept template void copy(From& f, To& t) { for(int x = f.get(); !f.eof(); x = f.get()) t.put(x); } struct Range { char begin, end; Range(char b, char e) : begin(b), end(e) { } int get() { return eof() ? -1 : begin++; } bool eof() { return begin > end; } }; int main(int, char**) { Range r('a', 'z'); copy(r, cout); return 0; }
20
20 Same Program w/ Inheritance void copy(istream& f, ostream& t) { for(int x = f.get(); !f.eof(); x = f.get()) t.put(x); } struct Range : istream { // Does istream have virtual methods? // If it does, which one of these should be overridden // int istream::peek() ? // int istream::get() // istream& istream::ignore(streamsize num, int delim) ? // istream& istream::get(char& c) // istream& istream::putback(char c) // istream& istream::unget(char c) // istream& istream::get(char* buf) // istream& istream::getline(char* buf, streamsize num) // istream& istream::read(char* buf, streamsize num) // streamsize istream::gcount() // pos_type istream::tellg() //... }
21
21 Cryptic Error Messages struct Num { // The problem: Copy c'tor is not public public: Num() { } private: Num(const Num& other) { } }; template void f() { vector > matrix; matrix.push_back(vector ()); } template void g() { (new V())->push_back(T()); } template void hh() { g (); f (); } template void h() { hh,T>(); } int main(int, char**) { h (); return 0; }
22
22 Cryptic Error Messages (Cont.) x.cpp: In function ‘void std::_Construct(_T1*, const _T2&) [with _T1 = Num, _T2 = Num]’: /usr/lib/gcc/i486-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/stl_uninitialized.h:86: instantiated from ‘_ForwardIterator std::__uninitialized_copy_aux(_InputIterator, _InputIterator, _ForwardIterator, __false_type) [with _InputIterator = __gnu_cxx::__normal_iterator > >, _ForwardIterator = Num*]’ /usr/lib/gcc/i486-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/stl_uninitialized.h:113: instantiated from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator > >, _ForwardIterator = Num*]’ /usr/lib/gcc/i486-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/stl_uninitialized.h:254: instantiated from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator ) [with _InputIterator = __gnu_cxx::__normal_iterator > >, _ForwardIterator = Num*, _Tp = Num]’ /usr/lib/gcc/i486-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/stl_vector.h:234: instantiated from ‘std::vector ::vector(const std::vector &) [with _Tp = Num, _Alloc = std::allocator ]’ x.cpp:15: instantiated from ‘void f() [with T = Num]’ x.cpp:22: instantiated from ‘void hh() [with V = std::vector >, T = Num]’ x.cpp:25: instantiated from ‘void h() [with T = Num]’ x.cpp:27: instantiated from here x.cpp:9: error: ‘Num::Num(const Num&)’ is private /usr/lib/gcc/i486-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/stl_construct.h:81: error: within this context x.cpp: In function ‘void std::_Construct(_T1*, const _T2&) [with _T1 = Num, _T2 = Num]’: /usr/lib/gcc/i486-linux-gnu/4.1.2/../../../../include/c++/4.1.2/bits/stl_uninitialized.h:86: instantiated from ‘_ForwardIterator std::__uninitialized_copy_aux(_InputIterator, _InputIterator, _ForwardIterator, __false_type) [with _InputIterator = __gnu_cxx::__normal_iterator > >, _ForwardIterator = Num*]’...
23
23 Documenting Concepts Understanding the type requirements from error messages is very difficult The solution: documentation Here are two examples from SGI's STL web-site http://www.sgi.com/tech/stl/Assignable.html http://www.sgi.com/tech/stl/UnaryFunction.html
24
24 Concept: Assignable Refinement of - Models int Notation X A type that is a model of Assignable x,y Object of type X Valid expressions Name Syntax Return-type Post-condition ======================================================= Copy-ctor X(x) X X(x) is a copy of x Assignment x = y X& x is a copy y Swap swap(x,y) void Same as: X t = x; x = y; y = t
25
25 Concept: Unary Function Refinement of Assignable Models char (*)(int) Notation F A type that is a model of Unary Function X The argument type of F Result The result type of F f Object of type F x Object of type X Valid expressions Name Syntax Return-type Post-condition ======================================================= call f(x) Result Return value is in f's range
26
26 Concept: Printable Refinement of Assignable Models ??? Notation P,Q A type that is a model of Printable p,q Object of type P, Q (respectively) Valid expressions Name Syntax Return-type Post-condition ======================================================= ctor P p(q) P p has a copy of q print p.print() void print() sent to p's copy of q
27
27 Can we Model the Printable Concept? class Printable { ??? }; struct X { print() {... } }; struct Y { print() {... } }; struct Z { }; vector vec; vec.push_back(X()); // OK -- X defines print() vec.push_back(Y()); // OK -- Y defines print() Printable& p = vec[0]; // p references the copy of x inside the vector p.print(); // invoke print() on that copy of x p = vec[1]; // p references the copy of y inside the vector vec.push_back(Z()); // Error -- Z does not defines print() Printable q = Z(); // Error -- Z does not defines print()
28
28 Printable: the void* Approach struct Printable void* data; template Printable(const T& t) { t.print(); // Ugly data = new T(t); } virtual ~Printable() { delete data; } // Cannot delete a void pointer Printable(const Printable& p) { data = new ??? p.data; } // Which type to create? void print() { data -> ??? } // Cannot send print() to void* // Cannot downcast - there's no common superclass };
29
29 Printable: the Right Way struct Base { virtual ~Base() { } virtual Base* clone() const = 0; virtual void print() = 0; }; template struct Holder : Base { U* u; Holder(const U& arg) : u(new U(arg)) { } ~Holder() { delete u; } Base* clone() const { return new Holder(*u); } void print() { u->print(); } }; struct Printable { Base* holder; template Printable(const T& t) : holder(new Holder (t)) { } Printable(const Printable& p) : holder(p.holder->clone()) { } virtual ~Printable() { delete holder; } void print() { holder->print(); } };
30
30 Printable: The Assignment Operator struct Printable { Base* holder;... void swap(Printable& p) { Base* temp = holder; holder = p.holder; p.holder = temp; } Printable& operator=(const Printable& p) { Printable temp(p); swap(temp); return *this; } };
31
31 Is This a Good Alternative? No! struct Printable { Base* holder;... Printable& operator=(const Printable& p) { if(this == &p) return *this; delete holder; holder = p.holder.clone(); return *this; } };
32
32 C++ Templates: Main Points A functional programming language Turing complete Has the same power as any other programming language Runs while the compiler compiles We build a tree (of types) at compile-time When an error occurs the compiler prints the path from the root of this tree
33
33 C++ Polymorphism: Templates vs. Inheritance Templates - pros: Type safety - as in collections Parametrize the protocol of a class Easier to define a compatible type Require only structural conformance A type parameter can serve as a super class Mixin Faster Inheritance – pros: Polymorphism happens at run-time E.g.: type of object is determined by an input value Readable error messages No executable blow up Separate compilation (templates must be #included)
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.