730 likes | 741 Vues
Learn how templates in C++ allow for the parameterization of functions and classes, enabling code reuse and genericity.
E N D
Templates and Standard Containers Chapter 6
Templates • Templates allow functions and classes to be parameterized • type of data being operated upon (or stored) is received via a parameter. • Templates provide a means to reuse code • One template definition can be used … • to create multiple instances of a function (or class), • each operating on (storing) a different type of data. • The template mechanism is important and powerful. • It is used throughout the Standard Template Library (STL) to achieve genericity.
Functions • Rationale for functions: • Make pieces of code reusable • Encapsulating them within a function. • Consider the task of swapping the values stored in two variables void Swap (int & first, int & second){int temp = first; first = second; second = temp; }
Integer Solution • Function Swap gives a general solution to the interchange problem for ints • This Swap function can be used to exchange the values of any two integer variables • In contrast, inline code would have to be rewritten for each pair of integer variables to swap.
Swap for Doubles • To interchange the values of two double variables: • Can't use the preceding function; it swaps ints • Overloading allows us to define multiple versions of the same function: void Swap (double & first, double & second) • Different Swap functions are distinguished by the compiler according to a function's signature • name, number, type and order of parameters
Swap for Strings • To interchange the values of two string variables: • Again, overload function Swap(): void Swap(string & first, string & second){string temp = first; first = second; second = temp; }
Swap Library • Why not create a library of all possible types to swap • Would work for predefined C++ types • but not for user defined types • must overload for each user defined type void Swap(Time & first, Time & second){Time temp = first; first = second; second = temp; }
Observations • The logic for each version of the Swap function is exactly the same • The only difference is in the type of the values being exchanged • Consider passing the type as a parameter! • Write a general solution • Could be used to exchange two values of any type
Template Mechanism • Declare a type parameter • also called a type placeholder • Use it in the function instead of a specific type. • This requires a different kind of parameter list: void Swap(______ & first, ______ & second) {________ temp = first; first = second; second = temp;}
Template Mechanism • The word template is a C++ keyword • Specifies that what follows is … • a pattern for a function • not a function definition. • “Normal” parameters (and arguments) • appear within function parentheses • Type parameters (and arguments for class templates) • appear within template angle brackets (< > ).
Template Mechanism • A function template cannot be split across files • specification and implementation must be in the same file • A function template is a pattern • describes how specific functions is constructed • constructed based on given actual types • type parameter said to be "bound" to the actual type passed to it
Template Mechanism • Each of the type parameters must appear at least once in parameter list of the function • compiler uses only types of the arguments in the call • thus determines what types to bind to the type parameters
Function Template template <typename ElementType> void Swap (ElementType &first, ElementType &second) { ElementType hold = first; first = second; second = hold; }
Function Template template <typename ElementType> void Swap (ElementType &first, ElementType &second) • Originally, the keyword class was used instead of typename in a type-parameter list. • "class" as a synonym for "kind" or "category"— specifies "type/kind" of types.
Function Template • <typename ElementType> names ElementType as a type parameter • The type will be determined … • by the compiler • from the type of the arguments passed • when Swap() is called.
General Form of Template template <typename TypeParam>FunctionDefinition or template <class TypeParam>FunctionDefinition where: TypeParam is a type-parameter (placeholder) naming the "generic" type of value(s) on which the function operates FunctionDefinition is the definition of the function, using type TypeParam.
Template Instantiation • A function template is only a pattern • describes how individual functions can be built from given actual types. • This process of constructing a function is called instantiation. • In each instantiation, the type parameter is said to be bound to the actual type passed. • A template thus serves as a pattern • for the definition of an unlimited number of instances.
Template Instantiation • In and of itself, the template does nothing. • When the compiler encounters a template • it stores the template • but doesn't generate any machine instructions. • Later, when it encounters a call to Swap() • Example: Swap(int1, int2); • it generates an integer instance of Swap()
Template Instantiation • Algorithm for instantiation • Search parameter list of function template for type parameters. • If one is found, determine type of corresponding argument. • Bind these types. • The compiler must actually "see" the definition of the function • not just the prototype • thus the requirement – a function template cannot be split across two files
Programming Style Tip • For efficiency (and often correctness), compilers frequently recommend: Explicit instantiation: Provide a prototype of function to be later instantiated to the compiler. • void Swap(int, int); void Swap(double, double); void Swap(string, string); void Swap(Time, Time);
Exercise • Write a function template which will find the largest element in an array • Write a driver program which will test this with at least three different instantiations • Apply concepts and tips presented in the lecture
Templates With Multiple Parameters Given: template <typename Type1, typename Type2 >void whatEver (Type1, Type2){ … } • Each type parameter must appear • at least once • in the function's parameter list. • Compiler must be able to determine actual type corresponding to each type parameter from a function call. Why?
Templates With Multiple Parameters • Example template <typename Type1, typename Type2>void Convert(Type1 value1, Type2 & value2){ value2 = static_cast<Type2>(value1);} • What does this function do? • Write a driver program to try it out
Templates With Multiple Parameters • The following version of function template Convert would not be legal … Why?? template <typename Type1, typename Type2>Type2 Convert(Type1 value1) { return static_cast<Type2>(value1);} Type2 is not in the parameter list
Solution • We could provide a dummy second parameter indicating the type of the return value: template <typename Type1, typename Type2>Type2 Convert(Type1 value1, Type2 value2){ return static_cast<Type2>(value1);} • Function call: double x = 3.14; int ix = Convert(x, 0);
Another Solution #include <iostream>using std::cout;using std::endl;template<typename type1, typename type2>type1 convert(type2 value){ return static_cast<type1>(value);}int main(){ float pi=3.14159F; cout << convert<int>(pi) << endl;}
Class Templates Recall our Stack class: const int STACK_CAPACITY = 128; typedef int StackElement; class Stack { /***** Function Members *****/ public: . . . /***** Data Members *****/ private: StackElement myArray[STACK_CAPACITY]; int myTop; }; How did we create a new version of a stack for a different type of element?
What’s wrong with typedef? • To change the meaning of StackElement • Merely change the type following typedef • Problems: • Changes the header file • Any program that uses this must be recompiled • A name declared using typedef can have only one meaning. • What if we need two stacks of different types in the same program? • cannot overload like functions (same number, type, and order of parameters)
StackElement is a “blank” type (a type placeholder)to be filled in later. template <typename StackElement> Type-Independent Container • Use a class template: • the class is parameterized • it receives the type of data stored in the class • via a parameter (like function templates). • Recall const int STACK_CAPACITY = 128; __________________________________ class Stack{/***** Function Members *****/public: . . ./***** Data Members *****/private:StackElement myArray[STACK_CAPACITY]; int myTop;};
General Form Of Class Template Declaration template <typenameTypeParam> or template <classTypeParam> class SomeClass{ // ... members of SomeClass ... }; More than one type parameter may be specified:template <typename TypeParam1,..., typename TypeParamn>class SomeClass{ // ... members of SomeClass ...};
Instantiating Class Templates • Instantiate it by using declaration of form ClassName<Type> object; • Passes Type as an argument to the class template definition. • Examples: Stack<int> intSt; Stack<string> stringSt; • Compiler will generate two distinct definitionsofStack • two instances • one for ints and one for strings.
Rules For Class Templates • Definitions of member functions outside class declarationmust be function templates. • All uses of class name as a type must be parameterized. • Member functions must be defined in the same file as the class declaration.
Applying the Rules to Our Stack Class • Recall how we specified the prototypes in the class • Used StackElement • Thus no changes needed – all rules OK • Apply Rule 1 • Each member functions definition preceded bytemplate <typename StackElement>
Applying the Rules to Our Stack Class • Apply Rule 2 • The class name Stack preceding the scope operator (::) is used asthe name of a type • must therefore be parameterized. template <typename StackElement>void Stack<StackElement>::push(const StackElement & value) { /* ... body of push() ... */ } • Apply Rule 3 : specification, implementation in same file
Applying the Rules to Friend Functions • Consider the addition of a friend functionoperator<< • Second parameter is of type Stack, must parameterized friend ostream & operator<<(ostream & out,const Stack<StackElement>& st);
Applying the Rules to Friend Functions • When defining the operator<< function • It must be defined as a function template template<typename StackElement> ostream & operator<<(ostream & out, const Stack<StackElement> & st) { . . . }
Exercise • Work together to complete a template version of the class Queue • Write a driver program to test it • Use at least three different types
STL (Standard Template Library) • A library of class and function templates Components: • Containers: • Generic "off-the-shelf" class templates for storing collections of data • Algorithms: • Generic "off-the-shelf" function templates for operating on containers • Iterators: • Generalized "smart" pointers that allow algorithms to operate on almost any container
begin() end() vector sort() Standard Template Library • Example of a specific • container class • iterator • algorithm
STL's 10 Containers Kind of ContainerSTL Containers Sequential:deque, list, vector Associative:map, multimap, multiset, set Adapters:priority_queue, queue, stack Non-STL:bitset, valarray, string
The vector Container • A type-independent pattern for an array class • capacity can expand • self contained • Declaration template <typename T> class vector { . . . } ;
The vector Container • Constructors vector<T> v, // empty vector v1(100), // 100 elements of type T v2(100, val), // 100 copies of val v3(fptr,lptr); // contains copies of // elements in memory // locations fptr to lptr
The vector Container • Vector operationsSee table on pg 270 • Vector object treated much like an array • without limitations of C-style arrays • Note how vector objects are self contained • able to manipulate their own "stuff"
Iterators • Note from table that a subscript operator is provided • BUT … this is not a generic way to access container elements • STL provides objects called iterators • can point at an element • can access the value within that element • can move from one element to another • They are independent of any particular container … thus a generic mechanism
v.begin() v.end() Iterators • Given a vector which has had values placed in the first 4 locations: • v.begin() will return the iterator value for the first slot, • v.end() for the next empty slot vector<int> v
Iterators • Each STL container declares an iterator type • can be used to define iterator objects • To declare an iterator object • the identifier iterator must be preceded by • name of container • scope operator :: • Example:vector<int>:: vecIter = v.begin()
Iterators • Basic operators that can be applied to iterators: • increment operator ++ • decrement operator -- • dereferencing operator * • Assignment = • Addition, subtraction +, -, +=, -=vecIter + n returns iterator positioned n elements away • Subscript operator [ ]vecIter[n] returns reference to nthelement from current position
for (vector<double>::iterator it = v.begin(); it != v.end(); it++) out << *it << " "; Iterators Contrast use of subscript vs. use of iterator ostream & operator<<(ostream & out, const vector<double> & v){ for (int i = 0; i < v.size(); i++) out << v[i] << " "; return out;}
Iterator Functions • Note table on pages 286, 287 • Note the capability of the last two groupings • Possible to insert, erase elements of a vector anywhere in the vector • Must use iterators to do this • Note also these operations are inefficient for arrays due to the shifting required