abstraction: generic programming , pt. 2

Post on 22-Jan-2016

40 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

Abstraction: Generic Programming , pt. 2. Templates. Overloading. Recall overloading Same function name used for different parameter types Compiler decides which function to use according to signature Legal to do in C++, not in C Allows for “generic” programming. Overloading. - PowerPoint PPT Presentation

TRANSCRIPT

Abstraction:Generic Programming, pt. 2

Templates

• Recall overloading

Same function name used for different parameter types

Compiler decides which function to use according to signature

• Legal to do in C++, not in C

• Allows for “generic” programming

Overloading

• Recall overloading

Same function name used for different parameter types

Compiler decides which function to use according to signature

• Can also be used with class hierarchy

• Subclass can override superclass function, handle subclass concerns

Overloading

Function Templates

• Acts like a blueprint for instantiating a function

– Type is abstracted in template

– Function is actually instantiated when it is used in program – then and only then is the type determined and bound to the abstract type(s) in the template

– Unlike overloading, the functions with same name are not all written out in code

Function Templates

Two implementations overload compare():int compare (const char &c1, const char &c2){ if (c1 < c2) return -1; if (c2 < c1) return 1; return 0;}

int compare (const int &i1, const int &i2){ if (i1 < i2) return -1; if (i2 < i1) return 1; return 0;}

Function Templates

One function template does the same thing!template <typename T>int compare (const T &v1, const T &v2){ if (v1 < v2) return -1; if (v2 < v1) return 1; return 0;}

Note that the template <typename T> declaration creates a scope in which T is known to the compilerCompare only requires “<” overloading for T

Function Templates

Now compare() can be called on for any two objects for which “<” is defined for their type!

int i1, i2;string s1, s2;double d1, d2;... cout << compare (i1, i2) << endl; // workscout << compare (s1, s2) << endl; // workscout << compare (d1, d2) << endl; // works

Function Templates

Now compare() can be called on for any two objects for which “<” is defined for their type!

When the code with compare is compiled, formal type T is replaced with actual type ...

It is as though you wrote the compare function three (or more) times, once for each type …

… but the compiler does it for you!

Function Templates

Function templates can be declared as

inlineConstexpr

Example:

template <typename T> inline T min(const T&, const T&);

Functions can return template type

Function Templates

Function templates can have multiple formal types

template <typename T, typename U> T fun(const T&, const U&);

Can use either “typename” or “class” - makes no difference

Function Templates

Can have non-type parameters in templates:

template <unsigned N, unsigned M>int compare(const char (&p1)[N], const char (&p2)[M]){ return strcmp(p1, p2);}compare(“Go”, “Gators”)

compiles to:int compare(const char (&p1)[3], const char (&p2)[7])

Function Templates

Compiling the template itself does not generate any code - - only when the function is actually used is code generated- hence function definition is needed in header with template (can't just give declaration)- compiler can only verify that the objects declared with the same formal type are declared with the same actual type when the template is encountered in code

Function Templates

Compiling the template itself does not generate any code - - only when the function is actually used is code generated- most errors only show up after instantiation- compiler can only catch syntax errors in template definition itself- compiler can check number and type agreement of arguments when use is seen- only on instantiation can type-related problems be detected – but where to report the error??? The code is generated!!!

Function Templates

Rules of thumb:- Make as few obligations on the types used as possible (“<” but not “>”, e.g.)- Use const if possible in parameters- When calling, make sure type used satisfies the obligations- A template not used with a type that does not satisfy obligations is NOT a problem!- Do your development with actual types before you convert to templates- Convert incrementally until you become comfortable with the process

Questions?

Class Templates

• Acts like a blueprint for instantiating a class

– Type is abstracted in template

– Compiler cannot deduce the type from use – must declare the type in angle brackets following the template's name

– Types in list are types to use in template formal type list

Class Templates

template <typename T> class Blob {public: typedef T value_type; // for decls typedef typename vector<T>::size_type size_type; Blob(); Blob(initializer_list<T>); size_type size() const {return data->size();} bool empty() const {return data->empty();} void push_back(const T &t) {data->push_back(t);} void pop_back(); T& back(); T& operator[](size_type);private: shared_ptr<vector<T>> data; void check(size_type, const string &msg) const;}

Class Templates

template <typename T> class Blob {public: typedef T value_type; // for decls typedef typename vector<T>::size_type size_type; Blob(); Blob(initializer_list<T>); size_type size() const {return data->size();} bool empty() const {return data->empty();} void push_back(const T &t) {data->push_back(t);} void pop_back(); T& back(); T& operator[](size_type);private: shared_ptr<vector<T>> data; void check(size_type, const string &msg) const;}

value_type is T, which will be declared when Blob is instantiated (as a class) with some

actual type (say, int or string).value_type and size_type are useful for

generic declarations relative to a particularclass without knowing what the details of

the class happen to be

Class Templates

template <typename T> class Blob {public: typedef T value_type; // for decls typedef typename vector<T>::size_type size_type; Blob(); Blob(initializer_list<T>); size_type size() const {return data->size();} bool empty() const {return data->empty();} void push_back(const T &t) {data->push_back(t);} void pop_back(); T& back(); T& operator[](size_type);private: shared_ptr<vector<T>> data; void check(size_type, const string &msg) const;}

Blob has two initializers – one default (for empty blob of desired type) and one with

initializer list (of multiple instances of objects or literals of desired type).

Initializer list can do the things initializer listsdo, like cause the same value to be repeated

N times, or list a bunch of different values.

Class Templates

template <typename T> class Blob {public: typedef T value_type; // for decls typedef typename vector<T>::size_type size_type; Blob(); Blob(initializer_list<T>); size_type size() const {return data->size();} bool empty() const {return data->empty();} void push_back(const T &t) {data->push_back(t);} void pop_back(); T& back(); T& operator[](size_type);private: shared_ptr<vector<T>> data; void check(size_type, const string &msg) const;}

Methods defined inside class template havethe scope of both the class AND the

template,so the class naming scope is assumed, and

the type(s) in the template can be used.

Class Templates

template <typename T> class Blob {public: typedef T value_type; // for decls typedef typename vector<T>::size_type size_type; Blob(); Blob(initializer_list<T>); size_type size() const {return data->size();} bool empty() const {return data->empty();} void push_back(const T &t) {data->push_back(t);} void pop_back(); T& back(); T& operator[](size_type);private: shared_ptr<vector<T>> data; void check(size_type, const string &msg) const;}

The type(s) in the template can be used in the bracketed list of other class templates

or functions

Class Templates

template <typename T> class Blob {public: typedef T value_type; // for decls typedef typename vector<T>::size_type size_type; Blob(); Blob(initializer_list<T>); size_type size() const {return data->size();} bool empty() const {return data->empty();} void push_back(const T &t) {data->push_back(t);} void pop_back(); T& back(); T& operator[](size_type);private: shared_ptr<vector<T>> data; void check(size_type, const string &msg) const;}

shared_ptr<U> is a “safe pointer” (see Ch 12)

that keeps track of how many pointers point to a dynamically allocated object, and delete

it when there are no pointers left.Use <memory> header.

Questions?

Class Templates

template <typename T> bool Blob<T>::empty() const { return data->empty();}

template <typename T>void Blob<T>::check(size_type index, const string &msg) const{ if (index >= data->size()) Throw out_of_range(msg);}

For functions defined outside of class, notonly must you include the class name for

scoping (Blob) with the scope operator (::), but you must also include the

formal type list (<T>) and first declare the whole thing to be a template (as you would

for a non-member function template)

Class Templates

template <typename T> bool Blob<T>::empty() const { return data->empty();}

template <typename T>void Blob<T>::check(size_type index, const string &msg) const{ if (index >= data->size()) Throw out_of_range(msg);}

The check() function serves to make sure an index is safe to use by throwing an

appropriate exception if it is not

Class Templates

template <typename T> T& Blob<T>::back() { check(0, “Back on empty Blob”); return data->back();}

template <typename T>T& Blob<T>::operator[](size_type index){ check(0, “Blob subscript out of range”); return (*data)[i];}

The template type T can be used as the return type, and can have all the usual

modifiers on it, like *, &, const, etc.

Class Templates

template <typename T> T& Blob<T>::back() { check(0, “Back on empty Blob”); return data->back();}

template <typename T>T& Blob<T>::operator[](size_type index){ check(0, “Blob subscript out of range”); return (*data)[i];}

Both back () and subscript [] operator should be overloaded on const so that both

can return values to use when const is needed and when the value is to be changed

Class Templates

template <typename T> Blob<T>::Blob() : data(make_shared<vector<T>>()) {}

template <typename T>Blob<T>::Blob(initializer_list<T> il) : data(make_shared<vector<T>>(il) {}

Both initializers make use of safe pointer structures – shared pointer and instantiation using make_shared so automatic garbage

collection can be done.Typename T is carried forward to the type list

needed by vector when it is “instantiated”

Class Templates

template <typename T> class BlobPtr {public: BlobPtr(): curr(0) {} T& operator*() const { auto p = check(curr, “Dereference past end”); return (*p)[curr]; // (*p) is the vector ... BlobPtr& operator++(); BlobPtr& operator--();

private: shared_ptr<vector<T>> check(size_t, const string&) const; weak_ptr<vector<T>> wptr; size_t curr; // current position in array}

Here we have another class template for pointers to classes instantiated from the

Blob class template.curr keeps track of where we are in the

objectthe pointer is pointing at.

Class Templates

template <typename T> class BlobPtr {public: BlobPtr(): curr(0) {} T& operator*() const { auto p = check(curr, “Dereference past end”); return (*p)[curr]; // (*p) is the vector ... BlobPtr& operator++(); BlobPtr& operator--();

private: shared_ptr<vector<T>> check(size_t, const string&) const; weak_ptr<vector<T>> wptr; size_t curr; // current position in array}

Overloading the dereference operator *Check now returns a shared pointer (safe) so long as it does not point to someplace

off the end of the storage

Class Templates

template <typename T> class BlobPtr {public: BlobPtr(): curr(0) {} T& operator*() const { auto p = check(curr, “Dereference past end”); return (*p)[curr]; // (*p) is the vector ... BlobPtr& operator++(); BlobPtr& operator--();

private: shared_ptr<vector<T>> check(size_t, const string&) const; weak_ptr<vector<T>> wptr; size_t curr; // current position in array}

Note that BlobPtr& type for the declarations of ++ and -- operators does NOT require the type parameter list <T> as needed

outside the class declaration

Class Templates

template <typename T> class BlobPtr {public: BlobPtr(): curr(0) {} T& operator*() const { auto p = check(curr, “Dereference past end”); return (*p)[curr]; // (*p) is the vector ... BlobPtr& operator++(); BlobPtr& operator--();

private: shared_ptr<vector<T>> check(size_t, const string&) const; weak_ptr<vector<T>> wptr; size_t curr; // current position in array}

When defined outside the class declaration, they would have to be:

BlobPtr<T>& operator++() {...} BlobPtr<T>& operator--() {...}

Class Templates

Postfix ++ operator defined outside class template body:template <typename T> BlobPtr<T> BlobPtr<T>::operator++(int) { BlobPtr retVal = *this; // remember old value ++*this; // increment value return retVal;}

When defined outside the class declaration, must includetype list for return type as well

as for naming scope

Questions?

Class Template Friends

template <typename> class BlobPtr;template <typename> class Blob;template <typename T>bool operator==(const Blob<T>&, const Blob<T>&);template <typename T> Class Blob {{ friend class BlobPtr<T>; friend bool operator==<T> (const Blob<T>&, const Blob<T>&); ...} Forward declarations needed for

friend declarations in BlobForward declaration needed for

parameters in operator==

Class Template Friends

template <typename> class BlobPtr;template <typename> class Blob;template <typename T>bool operator==(const Blob<T>&, const Blob<T>&);template <typename T> Class Blob {{ friend class BlobPtr<T>; friend bool operator==<T> (const Blob<T>&, const Blob<T>&); ...}

Grants friend access to versions of BlobPtr and operator== instantiated

in same type

Class Template Friends

template <typename> class Blob;template <typename T>ostream& operator<<(ostream&, const Blob<T>&);template <typename T> Class Blob {{ friend ostream& operator<< <T> (ostream&, const Blob<T>&); ...}template <typename T>ostream& operator<<(ostream& os, const Blob<T>& b){ ...}

Forward declaration needed for parameters in operator<<

Typename list does not need “formal types” since it is forward decl

Class Template Friends

template <typename> class Blob;template <typename T>ostream& operator<<(ostream&, const Blob<T>&);template <typename T> Class Blob {{ friend ostream& operator<< <T> (ostream&, const Blob<T>&); ...}template <typename T>ostream& operator<<(ostream& os, const Blob<T>& b){ ...}

Forward declaration of operator<< for friend declaration in class

Class Template Friends

template <typename> class Blob;template <typename T>ostream& operator<<(ostream&, const Blob<T>&);template <typename T> Class Blob {{ friend ostream& operator<< <T> (ostream&, const Blob<T>&); ...}template <typename T>ostream& operator<<(ostream& os, const Blob<T>& b){ ...}

Friend declaration of operator<< with partial specialization in class

Class Template Friends

template <typename> class Blob;template <typename T>ostream& operator<<(ostream&, const Blob<T>&);template <typename T> Class Blob {{ friend ostream& operator<< <T> (ostream&, const Blob<T>&); ...}template <typename T>ostream& operator<<(ostream& os, const Blob<T>& b){ ...}

Definition of operator<< with template type for Blob

top related