1 parametric polymorphism polymorphism techniques c++ templates
Post on 21-Dec-2015
239 views
TRANSCRIPT
1
Parametric PolymorphismParametric Polymorphism
Polymorphism Techniques C++ Templates
2
Polymorphism: OverloadingPolymorphism: 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
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: CoercionPolymorhism: Coercion
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)Polymorhism: Inclusion (Subtyping)
5
struct Num { Num(int v = 0) : value(v) { } int value;};
struct Person { Person(const char* name) : value(name) { } const char* value;};
template<typename T>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: ParametricPolymorphism: Parametric
6
Taxonomy of PolymorphismTaxonomy of Polymorphism Ad-hoc
Overloading Coercion
Universal Inclusion (subtyping) Parametric
7
Parametric PolymorphismParametric 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
Template Instantiation in C++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
C++ Function TemplatesC++ Function Templates A function can have type parameters
template<typename T> T* create() { return new T(); } void f() { string* sp = create<string>(); }
we use create() thru explicit instantiation We specify the actual types inside a pair of < >
In some cases the compiler can deduce the template parameterstemplate<typename T> 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
A Template Taking a ConstantA Template Taking a Constanttemplate<int N>struct FixedArray { double values[N]; int size() { return N; } // Could be static};
int main(int, char**) { FixedArray<10> arr; arr.values[0] = 3.14; cout << "first element=" << arr.values[0] << endl; cout << "size=" << arr.size() << endl; return 0;}
11
A Template Taking a Function Pointer A Template Taking a Function Pointer template<void (*F)()>struct Caller { static void run() { F(); }};
void hi() { cout << "hi" << endl;}void bye() { cout << "bye" << endl; } int main(int, char**) { Caller<hi>::run(); Caller<bye>::run(); return 0;}
12
A Template Taking a TypeA Template Taking a Typetemplate<typename T>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<char*> StrPair; // Instantiate Pair, // pass char* as an argument
typedef Pair<int> IntPair; int main(int, char**) { StrPair sp; IntPair ip; sp.set("ab", "cd"); sp.print(); ip.set(10, 20); ip.print(); return 0;}
13
SpecializationSpecializationtemplate<typename T>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<bool> { 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<bool> pb; pb.set(true, false); pb.print(); Pair<char> pc; pc.set('x', 'y'); pc.print(); return 0;}
14
A Non Conforming Specialization is LegalA Non Conforming Specialization is Legaltemplate<typename T>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<bool> { void set(bool x, bool y) { v = (x?1:0) + (y?2:0); } public: int v;}; void doSomething() { Pair<bool> pb; pb.set(true, false); cout << pb.v << endl; Pair<char> pc; pc.set('x', 'y'); pc.print(); pb.print(); // Error. Pair<bool>::print() is undefined}
15
C++ Templates: Interim SummaryC++ 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
C++ Templates as a C++ Templates as a Programming LanguagesProgramming 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
A Non-Terminating CompilationA Non-Terminating Compilationtemplate<typename T>struct Loop{ typedef typename Loop<Loop<T> >::Temp Temp;};
int main(int, char**) { Loop<int> n; return 0;}
18
ConceptsConcepts 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
Modelling the “From” ConceptModelling the “From” Concept
template<typename From, typename To>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
Same Program w/ InheritanceSame Program w/ Inheritancevoid 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
Cryptic Error Messages Cryptic Error Messages struct Num { // The problem: Copy c'tor is not public public: Num() { } private: Num(const Num& other) { } };
template<typename T> void f() { vector<vector<T> > matrix; matrix.push_back(vector<T>()); }
template<typename V,typename T>void g() { (new V())->push_back(T()); }
template<typename V,typename T>void hh() { g<V,T>(); f<T>(); }
template<typename T>void h() { hh<vector<Num>,T>(); }
int main(int, char**) { h<Num>(); return 0; }
22
Cryptic Error Messages (Cont.)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<const Num*, std::vector<Num, std::allocator<Num> > >, _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<const Num*, std::vector<Num, std::allocator<Num> > >, _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<_Tp>) [with _InputIterator = __gnu_cxx::__normal_iterator<const Num*, std::vector<Num, std::allocator<Num> > >, _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<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = Num, _Alloc = std::allocator<Num>]’
x.cpp:15: instantiated from ‘void f() [with T = Num]’
x.cpp:22: instantiated from ‘void hh() [with V = std::vector<Num, std::allocator<Num> >, 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<const Num*, std::vector<Num, std::allocator<Num> > >, _ForwardIterator = Num*]’
...
23
Documenting ConceptsDocumenting 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
Concept: AssignableConcept: Assignable
Refinement of-
Modelsint
NotationX A type that is a model of Assignablex,y Object of type X
Valid expressionsName Syntax Return-type Post-condition=======================================================Copy-ctor X(x) X X(x) is a copy of xAssignment x = y X& x is a copy ySwap swap(x,y) void Same as: X t = x; x = y; y = t
25
Concept: Unary FunctionConcept: Unary FunctionRefinement of
Assignable
Modelschar (*)(int)
NotationF A type that is a model of Unary FunctionX The argument type of FResult The result type of Ff Object of type Fx Object of type X
Valid expressionsName Syntax Return-type Post-condition=======================================================call f(x) Result Return value is in f's range
26
Concept: PrintableConcept: PrintableRefinement of
Assignable
Models???
NotationP,Q A type that is a model of Printablep,q Object of type P, Q (respectively)
Valid expressionsName Syntax Return-type Post-condition=======================================================ctor P p(q) P p has a copy of qprint p.print() void print() sent to p's copy of q
27
Can we Model the Printable Concept?Can we Model the Printable Concept?
class Printable { ??? };
struct X { print() { ... } };struct Y { print() { ... } };struct Z { };
vector<Printable> 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 vectorp.print(); // invoke print() on that copy of xp = 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
Printable: the void* ApproachPrintable: the void* Approachstruct Printable void* data;
template<typename T> 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};
29Printable: the Right WayPrintable: the Right Waystruct Base { virtual ~Base() { } virtual Base* clone() const = 0; virtual void print() = 0;};
template<typename U>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<typename T> Printable(const T& t) : holder(new Holder<T>(t)) { }
Printable(const Printable& p) : holder(p.holder->clone()) { }
virtual ~Printable() { delete holder; } void print() { holder->print(); }};
30
Printable: The Assignment OperatorPrintable: 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; }};
31Is This a Good Alternative? No!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
C++ Templates: Main PointsC++ 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
C++ Polymorphism: C++ Polymorphism: Templates vs. InheritanceTemplates 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)