Programming with ANSI C ++ A Step-by-Step Approach Prof. Bhushan Trivedi Director GLS Institute of Computer Technology
Chapter 7 Templates
Identical Body and different arguments BubbleSort(IntArray) BubbleSort(EmpArray) BubbleSort(StringArray) All three of above need same code to operate on diff types of data
Manually overloading is error prone We may forget to overload a function for a specific type We may have inadvertently change the content of the body of one of the overloaded function
The Function Templates Enables automatic instantiation Template functions are also known as generic functions Converting from a specific function to a generic one is a simple job
Conversion from a normal function to a template function Add the template definition before the function Convert normal type to generic wherever necessary Type name or class can be used to describe the generic type
Single argument function templates void BubbleSort(int TempIntArray[]) template <typename Type> void GenericBubbleSort(Type TempIntArray[]) 5/10/2018 The C2C programme
The Instantiation The definition of the GenericBubbleSort() does not define any function in true sense. When function is invoked or function address is taken then instantiation is done Compiler automatically generate correct code for the function actually used 5/10/2018 The C2C programme
Instantiation Continue.. Explicit casting for resolving ambiguity Template argument deduction Error when unique type can not be determined GenericBubbleSort <char> (Array3); // explicit argument--will create char instance GenericBubbleSort <int> (Array2); // explicit argument --will create int instance
Instantiation examples: compiler deducing arguments float Array4[10]; GenericBubbleSort(Array4); // will deduce float and generate float instance of GenericBubbleSort() i.e. GenericBubbleSort <float> ()
Instantiation examples: Generating an overloaded function or not GenericBubbleSort <char> (Array3); GenericBubbleSort <char> (Array5); The second call does not create the new overloaded function
Argument Deduction When the function is called, compiler looks at the type and values of the arguments passed to the function; and deduce the type of generic parameter looking at them
Argument Deduction template<typename Type> SumIt(Type One, Type Two) { return One + Two; } SumIt(12, 15); SumIt(string(“Jay”), string (“Hind”))
Argument Deduction SumIt (12, 12U) will not work! 12 is int and 12U is unsigned. Type placeholder can not have two values at the same point of time. Compiler can’t instantiate a function with unique Type value in this case.
Need to overload operators If we need to have our bubble sort to work with other then built-in data types, the operations like =, < and > are all important to be implemented in class representing that data type. Otherwise statements like TempIntArray[i] = TempIntArray[j] does not work properly
Need to overload operators if (TempGenericArray[i] < TempGenericArray[j] ) also will not compile. We also need to overload << if the data type is to be used with cout bool operator < (employee & OtherEmployee) { return (EmpNo > OtherEmployee.EmpNo); }
Multiple arguments: The search Example template <typename Type> int GenericSearch(Type TempGenericArray[], Type EleToBeSearched)
Multiple arguments: The search Example { for (int i=0 ; i < 10; i++) { if (EleToBeSearched == TempGenericArray[i]) return i; } //end of for loop return -1; } //end of program
Two Generic Arguments template <typename Type1, typename Type2> void BiggerSize(Type1 FirstVal, Type2 SecondVal) { if (sizeof(FirstVal) > sizeof (SecondVal)) cout << "First item's type is bigger\n"; else cout << "Second item's type is bigger\n"; } }
Non-type Parameters template <typename Type, int Size> void GenericBubbleSort(Type (&TempGenericArray)[Size]) In the case of following statement GenericBubbleSort (Array2); The generated function will not contain the size as integer variable but an actual size value
Non-type Parameters GenericBubbleSort() does not contain size variable but value 16 (deduced by compiler) as size parameter as it is the size of the array passed to the function.
Explicit Specialization friend ostream & operator << (ostream & TempOut, employee & TempEmployee); template <> friend void GenericBubbleSort (employee TempEmployee[], int Size);
Template Compilation Models Definition and instantiation is done at different places! Declarations can also be presented Inclusion Compilation Model Keeping the functions entirely Separation Compilation Model Keeping only declarations The export Keyword
Overloading function templates Template <typename Type> Type min (Type, int) Type min (const Type*, int)
Specialization Overloading a template function with a normal function is Specialization template <typename T> bool Max (T First, T Second) { return (First > Second);}
Specialization Following is a specialization for the C type string (Char arrays) template <> bool Max(char *First, char *Second) { return (strcmp(First,Second)>0); }
Overloading the generic function with another generic function template <typename Type> void GenericBubbleSort(Type TempGenericArray[], int Size)
Overloading the generic function with another generic function template <typename Type> void GenericBubbleSort(Type TempGenericArray[], int Size, Type TempResultArray[])
overloaded functions and templates template<typename Type> Type SumIt(Type, Type) int Main() { int i1, i2; char ch1, ch2; unsigned u1, u2;
Overloaded functions and templates SumIt(i1,i2); // int version instantiated SumIt(ch1,ch2); // char version instantiated SumIt(u1,u2); // unsigned version instantiated }
Using Default Arguments Efficiency (unexpected code bloat) Flexibility (need for operator overloading)
Class Templates It is also possible to have generic classes in C++ like generic functions When we think of stack or queue or collection we do not think of data types Such concepts don't change with type When we define the concepts, it is preferable to define them without specifying the type
The Generic Stack Class template <typename ElementType> class Stack { private: int StackPointer; ElementType StackArray[10];
The Generic Stack Class public: Stack() {StackPointer = 0; } void push(ElementType value) { if (StackPointer == 9) { cout << "Stack Overflow! Can't Insert!"; }
The Generic Stack Class else { StackArray[StackPointer]= value; StackPointer++; } } ElementType pop() { if (StackPointer == 0) cout << "can not pop";
The Generic Stack Class else { StackPointer--; return StackArray[StackPointer]; } } };
The Main Stack <int> MyStack; Stack <char> YourStack; MyStack.push(1); MyStack.push(2); cout << MyStack.pop()<< "\n";
YourStack.push('n'); The Main YourStack.push('O'); cout << YourStack.pop()<< "\n";
Changes in defining a class class Stack to template <typename ElementType> class Stack -------and----------- Stack MyStack changes to Stack <int> MyStack
Changes in the code Wherever we have used int as an element type in the earlier case, an argument to push and return type of pop, we have replaced here that by ElementType
Defining a member function outside template <typename ElementType> void Stack<ElementType>:: Push(ElementType value) ElementType Stack<ElementType>::pop()
The Template Class when we define template <Type ElementType> we are telling compiler that the following class definition contains a generic data type called ElementType. This does not define the class in a true sense.
The Instantiation Compiler creates the class only when object of the template class is defined. It creates the integer version of Stack class template (known as Stack <int>) at that moment.
The Instantiation It also creates an object of that class (MyStack) here. Thus creation of a class and the object is done together. The class is Stack <int> and the object is MyStack.
The Instantiation of the Class When the class is instantiated No functions are generated No data elements are created.
Instantiations and Specializations This process (generating normal class from the template class) is also known as instantiation. Here it is known as class instantiation from a class template. Both Stack <int> and Stack <char> are also known as specializations
Instantiations and Specializations If we would like Stack <employee> class to behave differently then Stack template, we can define that as an explicit specialization like we have done with template functions.
Explicit Specialization template <> class Stack <employee> { the body of the class here}
The Push template <> void Stack<employee>::push(employee TempEmp) { …. EmpNoArray[StackPointer]= TempEmp.EmpNo; StackPointer++; }
The Pop template <> employee Stack <employee>:: pop() { … StackPointer--; int TempEmpNo = EmpNoArray[StackPointer];
The Pop int SearchIndex = GenericSearch(UniEmployee, employee (TempEmpNo,"","",""));
Multiple Generic Data Types template <typename Type1, typename Type2> class ClassWithTwoTypes { Type1 FirstValue; Type2 SecondValue;
The Constructor public : ClassWithTwoTypes(Type1 TempVal1, Type2 TempVal2) { FirstValue = TempVal1; SecondValue = TempVal2; }
Multiple Generic Data Types void Display() { cout << FirstValue << " "<< SecondValue; } };
void main() { ClassWithTwoTypes<int, char> ObjectIC(12,'b'); ClassWithTwoTypes<char, string> ObjectIS('b',"Batsman"); ObjectIC.Display(); ObjectIS.Display();}
Using non-type arguments template <typename Type, int Size> class SafeGenericArray { Type Array[Size]; ……………….} SafeGenericArray<int,5> SafeIntArray;
Default Arguments template <typename Type = int, int Size = 10> class SafeGenericArray SafeGenericArray<int,5> SafeIntArray1;
SafeGenericArray<char> SafeCharArray1;// Size = 10 Default Arguments SafeGenericArray<char> SafeCharArray1;// Size = 10 SafeGenericArray<> SafeIntArray2; // Type = int and Size = 10 SafeGenericArray<char,5> SafeCharArray2;
Static data members The static variable will have one single instance for one initialization of the template class. For Stack<int> and for Stack<char> we have two different static members.
Static data members template <typename ElementType> int Stack<ElementType>::TotalStacks; Stack <int> MyStack1; Stack <char> YourStack1; cout << Stack<int>::TotalStacks<<"\t"; cout << Stack<char>::TotalStacks<<"\n";
Primary and Partial Specialization What if we want to have a specialization of the array class that we have defined to work for pointer-to-any-variable in a different way? instead of storing pointers we may need to store the contents of them
Primary and Partial Specialization Normal overloading provides special treatment for a single type which can be valid type for our template. This is a situation where we are dealing with set of types (all type of pointers in our case) for which we need a different behavior.
Primary and Partial Specialization template <typename Type> class SafeGenericArray Primary Partial class SafeGenericArray <Type *>
Primary and Partial Specialization SafeGenericArray<char> SafeCharArray1; Primary Partial SafeGenericArray<char *> SafeStringArray
Compilation models for templates The difference between an inline function and a normal function called in multiple files The Template instantiations in multiple files The Export Keyword