1 / 40

Abstraction: Generic Programming , pt. 2

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.

tulia
Download Presentation

Abstraction: Generic Programming , pt. 2

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Abstraction:Generic Programming, pt. 2 Templates

  2. 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

  3. 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

  4. 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

  5. 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 (constint &i1, constint &i2) { if (i1 < i2) return -1; if (i2 < i1) return 1; return 0; }

  6. 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 compiler Compare only requires “<” overloading for T

  7. 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; // works cout << compare (s1, s2) << endl; // works cout << compare (d1, d2) << endl; // works

  8. 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!

  9. Function Templates Function templates can be declared as inline Constexpr Example: template <typename T> inline T min(const T&, const T&); Functions can return template type

  10. 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

  11. 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])

  12. 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

  13. 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!!!

  14. 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

  15. Questions?

  16. 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

  17. 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; }

  18. 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 particular class without knowing what the details of the class happen to be

  19. 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 lists do, like cause the same value to be repeated N times, or list a bunch of different values.

  20. 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 have the 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.

  21. 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

  22. 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.

  23. Questions?

  24. 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, not only 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)

  25. 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

  26. 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.

  27. 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

  28. 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”

  29. 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 object the pointer is pointing at.

  30. 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

  31. 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

  32. 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--() {...}

  33. Class Templates Postfix ++ operator defined outside class template body: template <typename T> BlobPtr<T> BlobPtr<T>::operator++(int) { BlobPtrretVal = *this; // remember old value ++*this; // increment value return retVal; } When defined outside the class declaration, must include type list for return type as well as for naming scope

  34. Questions?

  35. 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 Blob Forward declaration needed for parameters in operator==

  36. 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 sametype

  37. 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

  38. 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

  39. 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

  40. 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

More Related