260 likes | 523 Views
Introduction to C++ Programming Module 4 Function and Class Templates. Yaodong Bi, Ph.D. Department of Computing Sciences University of Scranton (717)941-6108 bi@cs.uofs.edu. Outline. Module 1 - An Overview of C++ and OO Programming Module 2 - Classes and Operator Overloading
E N D
Introduction to C++ ProgrammingModule 4Function and Class Templates Yaodong Bi, Ph.D. Department of Computing Sciences University of Scranton (717)941-6108 bi@cs.uofs.edu
Outline • Module 1 - An Overview of C++ and OO Programming • Module 2 - Classes and Operator Overloading • Module 3- Inheritance and Dynamic Binding • Module 4 - Function and Class Templates • Module 5 - C++ Stream I/O and Exception Handling
Review of Module 3 • Public Inheritance and Protected Members • Constructors and destructors in derived classes • Redefine Base-class Members in Derived Classes • Type Conversions • Polymorphism and Virtual Functions • Virtual Destructors • Abstract Classes and Concrete classes • Multiple Inheritance • Virtual Base Classes
Outline of Module 4 • Function Templates • Function Template Definition • Function Template Instantiation • Template Explicit Specialization • Overloading Function Templates • Template Compilation Models • Class Templates • Class Template Definition • Class Template Instantiation • Members of Class Templates • Friend Declarations in Class Templates • Class Template Compilation Model
// stackTmpl.h const int maxSize = 10; template <class T> class CStack { public: // public interface CStack(int sz = maxSize); void Push (const T& item); T Pop(); bool IsEmpty() const; bool IsFull() const; ~CStack(); private: // private implementation T* m_Array; int m_nTop; int m_nSize; }; // stack.cpp #include <iostream.h> #include "stackTmpl.h" template<class T> CStack<T>::CStack(int sz) { m_Array = new T[sz]; m_nSize = sz; m_nTop = -1; }; template<class T> void CStack<T>::Push(const T& item) { m_Array[++m_nTop] = item; }; template<class T> T CStack<T>::Pop() { return m_Array[m_nTop--]; }; Example 1: Templates
// stack.cpp - cont’d template<class T> bool CStack<T>::IsEmpty() const { return (m_nTop < 0); }; template<class T> bool CStack<T>::IsFull() const { return (m_nTop == m_nSize - 1); }; template<class T> CStack<T>::~CStack() { delete [] m_Array; }; // template.cpp #include <iostream.h> #include "stackTmpl.cpp” // function template template<class T> void Print(CStack<T>& stack) { while (!stack.IsEmpty()) { cout << stack.Pop() << endl; } } Example 1: Templates - cont’d
// template.cpp #include <iostream.h> #include "stackTmpl.cpp" template <class T> void Print(CStack<T>& stack); void main() { CStack<int> istack; CStack<char> cstack; for ( char i = 'A'; i < 'A'+10; i++) { if (!istack.IsFull()) istack.Push(int(i)); if (!cstack.IsFull()) cstack.Push(i); } Print(istack); Print(cstack); } Template Function template<class T> void Print(CStack<T>& stack) { … } Template Class template <class T> class CStack { … }; Class Template Instantiation CStack<int> istack; CStack<char> cstack; …. if (!istack.IsFull()) istack.Push(int(i)); if (!cstack.IsFull()) cstack.Push(i); Function Template Instantiation Print(istack); Print(cstack); Example 1: Templates - cont’d
Function Template Definition • Syntax template<parameter_list> ret_type func_name(fun_parameters) ex: template <class T, int sz> T min(T (&arr)[sz]); template <class T> void print(CStack<T>& stack) {} • Template parameters • template type parameters • Representing a type • Syntax: class type_name or typename type_name.ex: class T • Each type parameter must be preceded by class or typename. Ex: tempate<class T1, T2, int sz> T1 foo() {}// T2 needs class • template nontype parameters • Representing a constant expression • The value must be known at compile-time • Declared as a ordinary variable ex: int sz
Function Template Definition • Parameter names • The name of a template parameter can be treated as a locally-defined type/variable after it is declared. template <class T, T const_value, int sz> void foo(const T&). // The type parameter T is used in the parameter list template < T const_value, class T, int sz> void foo(const T&). // error: the first appearance of T is not declared yet • When a global object, function, or type uses the same name as a template parameter, the global scope name is hidden. typedef double Type; template <class Type> Type min(Type a, Type b) { … Type tmp; } // tmp has the type of the template parameter Type • A object or variable in the template function cannot have the same name as that of a template parameter. template <class Type> Type min(Type a, Type b) { … typedef double Type; } // error: re-declare template parameter Type
Function Template Instantiation • Instantiation • when the template is invoked. template <class T, int sz> T min(T (&arr)[sz]) {/* … */ } template <class T> void print(CStack<T>& stack) {/* … */ } int ia[] = {10, 11, 12, 15}; double da[] = {1.1, 1.2, 1.3}; CStack<int> iStack; CStack<CString> sStack …. int I = min(ia); // T => int, sz => 4; double d= min(da); // T =>double, sz = 3; print(iStack); // T => int print(sStack); // T => CString • When the address of the template is taken int (*pf)(int (&)[10]) = &min; // pf points to int min(int (&)[10]) void (*pff)(CStack<int> &) = &print; // pff points to void print(CStack<int>)
Function Template Instantiation • Template argument deduction • The types and values of the template arguments are determined by an examination of the types of the function arguments. Ex: template <class U, class U, class V> U foo(U, V) {} double d; int i; double d1=foo(d, i) // instantiate double foo(double, int) • The return type of the instantiation is not considered during template argument deduction. double da[3] = {1.1, 1.2, 1.3}; int i = min(da); // return a double, then convert it to int • If a template parameter occurs multiple times, each deducted type must exactly match the first type deducted. Ex: template <class T> T min(T, T) {} unsigned int ui; min(ui, 1024); // error: cannot instantiate min(unsigned, int); min(ui, unsigned(1024)) // okay: min(unsigned, unsigned);
Function Template Instantiation • Explicit Template Arguments • Explicitly specify the types of the template arguments ex1:template <class T> T min(T, T) {} min<unsigned, int> (ui, 1024); // min(unsigned, unsigned) instantiated // the second argument is converted to unsigned. ex2:template <class T, class U, class V> T foo(U, V) {} // T cannot be deduced from the function arguments int x = foo<int , char, unsigned > (ch, ui); // T is int. • In the explicit specification, we need only list the arguments that cannot be implicitly deduced. We can omit only trailing arguments. int x = foo<int> (ch, ui); // int foo(char, unsigned) int (*pf)(char, int) = &foo<int>; // int foo(char, int) int x = foo<int, ,char> (ch, char); // Error: trailing arguments only • When the template arguments are explicitly specified, there is no need to deduce the template arguments.
Template Explicit Specialization • When T is const char*, the following template doesn’t work! template<class T> T min(T t1, T t2) { return (t1 > t2 ? t1 : t2} // return the smaller between t1 and t2 • Define an explicitly specialization for const char* typedef const char* PCC; template<> PCC min<PCC> (PCC s1, PCC s2) { return (strcmp(s1, s2) > 0 ? s1 : s2); } • When min(const char*, const char*) is called, the above specialization will be invoked. For others, the generic template will be instantiated and invoked. int i = min(10, 5); // call to instantiation const char* p =min(“hello”, “world”); // call to explicit specialization • The specialization list may be omitted if the arguments can be deduced. template<> PCC min(PCC s1, PCC s2); //okay • The declaration of an specialization must be seen before it is used -- otherwise, compile-time error.
Overloading Function Templates • A function template can be overloaded. template <class U> class Array{ /* … */}; template<class T> T min(const Array<T>&, int); // #1 template<class T> T min(const T*, int); // #2 template<class T> T min(T, T); // #3 • The most specialized definition is chosen for an instantiation. Array<int> iArray(1024); int ia[1024]; … int ret1 = min(iArray, 1024); // #1 is most specialized (compared to #3), #2 is not applicable. int ret1 = min(ia, 1024); // #2 is most specialized, T=>int. In #3, T=>int* int ret1 = min(1.23, 2.34); // #3 is the one applicable. • Overloaded function templates may lead to ambiguities when a template instantiation is invoked.
Overloading Function Templates • A function template may also have the same name as an ordinary nontemplate function. Ex1: template <class T> T sum(T, int) {} // function template double sum(double, double) {} // nontemplate function … int i; double d; double d2 = sum(d, i); // Which one called? // since the template has a perfect match, the template is called. Ex2: template <class T> T sum(T, T) {} // function template double sum(double, double) {} // nontemplate function … double d1, d2; double d3 = sum(d1, d2); // Both have the perfect match. // when a template and an ordinary have the perfect match, // the ordinary nontemplate will be called. • Further reading is needed for this topic. Read Stroustrup’s and Lippman’s books.
Template Compilation Models • Inclusion compilation model // inclusion.cpp -- contains the template declaration and definition. template <class T> T min(T t1, T t2) { return t1 > t2 ? t1 : t2; } //main.cpp #include “inclusion.cpp” // the template file is included in all the files. double ret = min( 2, 20); • Separation compilation model // separate.h -- contains the template declaration only template <class T> T min(T t1, T t2); // separate.cpp --- the template definition #include “inclusion.h” export template <class T> T min(T t1, T t2) { return t1 > t2 ? t1 : t2; } // main.cpp --- the user #include “inclusion.h” // the header file is included in all the files. // only the declaration is included. double ret = min( 2, 20); • MS Visual C++ 5.0 does not seem to support the separation model
Class Template Definition • Syntax template <class T> class CStack {… void Push (const T& item); T Pop(); …. T* m_Array; }; • Template parameters • template type parameters • Representing a type • Syntax: class type_name or typename type_name.ex: class T • Each type parameter must be preceded by class or typename. Ex: tempate<class T1, T2, int sz> class foo {}// T2 needs class • template nontype parameters • Representing a constant expression • The value must be known at compile-time • Declared as a ordinary variable ex: int sz • Example: template <class Type, int size> class buffer;
Class Template Definition • Parameter names • The name of a template parameter can be treated as a locally-defined type/variable after it is declared. template <class T, T const_value, int sz> class foo {..}; // okay • When a global object, function, or type uses the same name as a template parameter, the global scope name is hidden. typedef double Type; template <class Type> class foo { … Type tmp; } // tmp is of the template parameter Type, not global typedef’ed Type • A object or variable in the template function cannot have the same name as that of a template parameter. template <class Type> class foo { … typedef double Type; } // error: re-declare template parameter Type • Default arguments • Both type and nontype parameters can have default arguments template <class Type, int size = 1024> class foo {...};
Class Template Instantiation • Instantiation • The name of a class template instantiation must always specify the template arguments explicitly. CStack<int> istack; // istack is a stack of integers CStack<CString> strStack; // strStack is a stack of CStrings • Objects from instantiations can be used the same way as objects of nontemplate classes. iStack.Push(int_item); CString str = strStack.Pop(); • A template declaration or definition can refer to a class template or to an instantiation of a class template. template <class Type> void foo(CStack<Type>, // generic template CStack<int>) // template instantiation • In a nontemplate definition, only class template instantiation can be used. • void foo(CStack<int> & istack) {…}//must be instantiated.
Member Functions of Class Templates • A member function may be defined within the class definition. template <class T> class CStack {… bool IsEmpty() const { return (m_nTop < 0); }; }; // The member function is defined as a regular nontemplate member. • A member function may be defined outside the class definition. template<class T> bool CStack<T>::IsEmpty() const { return (m_nTop < 0); }; // it is necessary to include <parameter_list> in the scope specifier. template<class T>T CStack<T>::Pop() { return m_Array[m_nTop--]; }; // T can be used as a regular class (as the return type) in the def. template<class T> CStack<T>::CStack(int sz) { m_Array = new T[sz]; m_nSize = sz; m_nTop = -1; }; • A member function of a class template is not instantiated automatically when the class template is itself instantiated. The member function is instantiated only if it is used by the program.
Friend Declarations in Class Templates • Three kinds of friend declarations • A nontemplate friend function or friend class class Foo { … void bar(); …}; template <class T> class Queue { friend class foobar; // okay, even it has not been declared. friend ostream& operator<<(ostream& const CStack<T>&); friend void Foo::bar(); }; // Foo must be defined b/c its member is used. • A bound friend class or function template : one-to-one. template <class T> class foobar { … }; template <class T> void foo(Queue<T>); template <class T> class queueItem { friend class foobar<T>;// okay. friend void foo<T>(Queue<T>);// okay. friend void Foo<T>::bar(); • An unbound friend class or function template : one-to-many. template <class T> class queueItem { template <class U> friend class foobar;// all instantiations are friend. template <class U> friend void foo(Queue<U>); }; // all instantiation.
Template Compilation Models • Inclusion compilation model // inclusion.h -- contains the class def. And member function definition. template <class T> class Foo{… void bar(); …}; template <class T> Foo<T>::bar() {…} //main.cpp #include “inclusion.h” // the template file is included in all the files. Foo<int> iObject; • Separation compilation model // separate.h -- contains the template declaration only export template <class T> class Foo {… void bar(); …}; // separate.cpp --- the template definition #include “inclusion.h” export template <class T> Foo<T>::bar() {…} // main.cpp --- the user #include “inclusion.h” // the header file is included in all the files. // only the declaration is included. Foo<int> iObject; • MS Visual C++ 5.0 does not seem to support the separation model
Summary of Module 4 • Function Templates • Function Template Definition • Function Template Instantiation • Template Explicit Specialization • Overloading Function Templates • Template Compilation Models • Class Templates • Class Template Definition • Class Template Instantiation • Members of Class Templates • Friend Declarations in Class Templates • Class Template Compilation Model
Advice • Debug concrete examples before generalizing to a template. • Use templates to express algorithms that apply to many argument types. • Always declare the general form of a template before specialization. • Use specialization and overloading to provide a single interface to implementations of the same concept for different types. • Remember to export template definitions that need to be accessible from other translation units (files). • Use explicit instantiation to minimize compile time and link time.
Programming Assignments • Improve Example 1: Stack Template • Change the default size of the array to a template parameter. What is the difference from the original implementation? template <class T, int size> class CStack { …. }; • Modify the Push() and Pop functions. template <class T, int size> bool CStack<T, size> ::Push(const T&); template <class T, int size> bool CStack<T, size> ::pop(T&); • Write a template overloading the operator <<. It prints the content of the stack in a bottom-up fashion. template <class T, int size> ostream& operator<<(ostream&, const CStack<T, size>&); • Write a main() to test your new template. • Design a string template • Design a concrete class for strings of characters, debug the class. • Convert the class into a template. • Overload I/O operators for the template. • Write a main() to test your new template.