290 likes | 299 Views
Learn about C++ templates and template instantiation, which allows for parametrized definitions at compile-time. Understand how template parameters and instantiations work, and explore function and class templates. Discover the use of different types of parameters in templates, such as type parameters, numerical constants, variables, and routines. Also, learn about template specialization and its role in creating customized template instances.
E N D
Genericity • C++ Templates
Parametric Polymorphism • Allows definitions to be parametrized at compile-time • Such a definition is actually a “function” that returns a new program element(method, class, etc.) • Template parameters = The arguments of this “function” • AKA: type parameters • Instantiation of a template == evaluating the “function”
Template Instantiation in C++ • The compiler recompiles the template definition • Substitutes the formal template parameters with the actual parameters • Generates new object code • Almost no compilation when the definition is read • Most compilation work is at each instantiation point • The template definition must be part of the source code • That is: #included from an .h file • Supporting separate compilation of templates is too difficult in practice
C++ Function Templates • A function can have type parameters template<typename T> T* create() { return new T(); } void f() { string* sp = create<string>(); } • we use create() thru explicit instantiation • We specify the actual types inside a pair of < > • In some cases the compiler can deduce the template parameters template<typename T> void print(T& t) { cout << t << endl; } void g() { print(5); } • We use print() thru implicit instantiation • Possible if a type parameter is also a value parameter • Make overload resolution more difficult • Introduced later
C++ Class Templates template<class Type> class Stack { Type buff[50]; int sp; public: Stack(void): sp(0) {} void push(const Type &e) { buff[sp++] = e;} Type pop(void) { return buff[--sp]; } int empty(void) const { return sp == 0;} int full(void) const{ return sp == 50; } }; template <class T> Stack<T>::push(T val){ if (sp >= size) { error("Stack is full"); return; } buff[sp++] = val; }
Kind of Parameters Generics are “routines executed at compile time and produce source code”. Therefore, all parameters must be entities that are known at compile time. • Type: most frequent and most important. All languages which support genericity allow type parameter. • Numerical Constant: e.g., the integer constant 3. • Useful for building generic arrays. • Variables: • Constant: Address of a variable in C++ • Routine: • Constant: Address of function in C++
A Template Taking a Constant template<int N> struct FixedArray { double values[N]; int size() { return N; } // Could be static }; int main(int, char**) { FixedArray<10> arr; arr.values[0] = 3.14; cout << "first element=" << arr.values[0] << endl; cout << "size=" << arr.size() << endl; return 0; }
A Template Taking a Function Pointer typedef void (*ErrorFunction)(const char *); template <class Type, ErrorFunction error> class Array { size_t n; Type *buff; public: Array(size_t n_): n(n_), buff(new Type[n]){ if ((buff == (Type *)0) error("Memory failure"); } Type & operator [] (size_t i) { if (i >= n){ error("Array overflow"); return buff[i]; } } ... };
Using Function Arguments to Templates #include <stdlib.h> #include <iostream.h> void err_abort(const char *msg) { cerr << "Error: " << msg <<". Aborting!\n"; exit(1); } typedef Array<int, err_abort> SafeIntArray; ... SafeIntArray a(20);
String Arguments to Templates? template <class Type, const char *Name> class NamedType: public Type { public: const char *name() { return Name; } }; #define NAMED(T) NamedType<T, #T> typedef NAMED(MyClass) MyNamedClass; Does not work! Consider constchar *const Hello1 = "Hello"; constchar *const Hello2 = "Hello"; Then, Hello1 and Hello2 are not (usually) the same!
A Template Taking a Type template<typename T> struct Pair { void set(const T& x, const T& y) { a = x; b = y; } void print() { cout << a << "," << b << endl; } private: T a, b; }; typedef Pair<char*> StrPair; // Instantiate Pair, // pass char* as an argument typedef Pair<int> IntPair; int main(int, char**) { StrPair sp; IntPair ip; sp.set("ab", "cd"); sp.print(); ip.set(10, 20); ip.print(); return 0; }
Specialization template<typename T> struct Pair { void set(const T& x, const T& y) { a = x; b = y; } void print() { cout << a << "," << b << endl; } private: T a, b; }; template<> struct Pair<bool> { void set(bool x, bool y) { v = (x?1:0) + (y?2:0); } void print() { cout << (v&1) << "," << (v&2) << endl; } private: int v; }; int main(int, char**) { Pair<bool> pb; pb.set(true, false); pb.print(); Pair<char> pc; pc.set('x', 'y'); pc.print(); return 0; }
A Non Conforming Specialization is Legal template<typename T> struct Pair { void set(const T& x, const T& y) { a = x; b = y; } void print() { cout << a << "," << b << endl; } private: T a, b; }; template<> struct Pair<bool> { void set(bool x, bool y) { v = (x?1:0) + (y?2:0); } public: int v; }; void doSomething() { Pair<bool> pb; pb.set(true, false); cout << pb.v << endl; Pair<char> pc; pc.set('x', 'y'); pc.print(); pb.print(); // Error. Pair<bool>::print() is undefined }
C++ Templates: Interim Summary • A template is a “function” • Arguments: types, constants • Return value : A new class or a new function • Recognized by the compiler • The template is recompiled with each Instantiation • Specialization == “if” • Different result based on the actual arguments
Compile Time Computations • template <int N> • struct Factorial • { • enum { value = N * Factorial<N - 1>::value }; • }; • template <> • struct Factorial<0> • { • enum { value = 1 }; • }; • void foo() • { • int x = Factorial<4>::value; // == 24 • int y = Factorial<0>::value; // == 1 • }
A Non-Terminating Compilation template<typename T> struct Loop { typedef typename Loop<Loop<T> >::Temp Temp; }; int main(int, char**) { Loop<int> n; return 0; }
Constraints on Parameters? • Constant parameters: type of argument. In C++, the usual type conversions are allowed, e.g., • if formal parameter to class template is of type T*, then actual parameter can be of type T1*,provided that T1 is derived from T. • Type of parameters: what is the type of a type? • Main approaches: • No restrictions • Dynamic checking • Explicit list of constraints • By derivation
No Restriction on Arguments • Any type is allowed. Generic limited to what can be done on all types. • Example: • Early versions of Eiffel. • Advantages: • Code sharing. • Disadvantages: • Restrictions on generics. • Only the most primitive operations are allowed on arguments.
Dynamic Checking of Arguments (The generic equivalent of dynamic typing.) The compiler tries to instantiate a template. If an invalid operation is attempted, then an error message is issued. • Example: • C++ • Advantages: • Compiler writing is easy • Flexibility to programmer • Disadvantages: • Extra burden on the library designer • Surprises to the user • Constraints are dependent on actual usage • Difficult to understand • Some parts of static type checking are deferred to link time
Explicit List of Constraints The programmer specifies the set of operations that might be used on a type argument. • Example: Ada • Advantages: • Readability • No surprises • Disadvantages: • Lists could be very long • Less flexibility in the design of classes • List of operations instead of abstraction of functionality
Constraints on Type Parameters (cont.) • By derivation. The actual parameter must be the same or inherited from the declared type. • Example: • Current Eiffel • Advantages: • Code sharing • OOP Like • Abstraction at the right level • Disadvantages: • Could lead to over use of inheritance • Inheritance is used for abstraction, not only for subtyping • Too specific parameter types • Too general parameter types • Not appropriate for built-in types
Quiz: What are the 6 Constraints? template <class T> T avg(const T a[], int size) { T sum = a[0]; for (int i = 1; i < size; i++) sum += a[i]; return sum/size; }
Quiz: The 5 Constraints template<class Type> class Stack { Type buff[50]; int sp; public: Stack(void): sp(0) {} void push(const Type &e) { buff[sp++] = e; } Type pop(void) { return buff[--sp]; } int empty(void) const { return sp == 0; } int full(void) const { return sp == 50; } };
Instantiation • Implicit: Instantiation occurs whenever an instance is required. • Examples: ML, Function Templates in C++. • Advantages: simple syntax. • Disadvantages: Surprises. Requires a complex type inference engine: can be in some conditions an undecidable problem. • Implicit Usage: Instantiation occurs whenever the programmer uses an instance of a generic for the first time. • Examples: Class templates in C++. Optional in function templates in C++ (since November 1993). • Advantages: Balance between convenience and accuracy. • Disadvantages: Confusing error messages. Relatively complex syntax. Obscure environment of instantiation. • Explicit: Programmer must instantiate a generic priorly to using it. • Examples: Ada. C++ new features: explicit instantiation. • Advantages: Explicit. No surprises. • Disadvantages: What if the same generic is instantiated twice?
Example: Function Template Instantiation template <class T> T max(T a,T b) { return a > b ? a : b; } int main() { extern int f(int); int i(10), j = f(i); int k = max(i, j); extern double cos(double); double r(0.5), s(cos(r)); double t = max(r, s); return max(k, int(1/t)); }
Function Return Type? The function return type is not used for resolving ambiguities template <class T> inline long double sum(const T a[], int size){ return (long double) avg(a, size) * size; } template <class T> inline T sum(const T a[], int size) { return avg(a, size) * size; } The compiler will complain if the sum template is used
Class Template Instantiation • Whenever a new particular version of the class template Stackis used (such as Stack<int>or Stack<char*>), the compiler will arrange for a version of that template to be instantiated. • Each instantiation of template class is a class by itself. • The template classes Stack<int>, Stack<Complex>, and Stack<void*>are different classes. • There may be compile time errors when a template is instantiated. • Only the members that are used should be instantiated. • In a single compilation unit, instantiation of a template occurs only once for each particular set of values of the arguments.
Explicit Instantiation in C++ New feature: adopted in San Jose, November 1993. • Ability to specify environment for instantiation. • Pre-create libraries of instantiations. Independent of changes in the environment of the using programs. Some implementations: multiple explicit instantiation may produce an error message or warning. • Explicit instantiation of a class template: forces instantiation of all its members, whereby forcing constraints. template class Stack<int>; • Explicit instantiation of a function member of a class template: template void Stack<int>::push(int &); • Explicit instantiation of a function template: ...
Explicit Instantiation of Function Template Given the function template:template <class T> T max(T a, T b) { return a > b ? a : b;} Explicit Instantiation is: template int max<int>(int, int); // naming the argument template int max(int, int); // the argument is deduced here