Understanding C++ Templates CNS 3370 Copyright 2006, Fresh Sources, Inc.

Slides:



Advertisements
Similar presentations
C++ How to Program, 7/e © by Pearson Education, Inc. All Rights Reserved.
Advertisements

Abstract Data Types Data abstraction, or abstract data types, is a programming methodology where one defines not only the data structure to be used, but.
Chapter 14: Overloading and Templates
Chapter 16 Templates. Copyright © 2006 Pearson Addison-Wesley. All rights reserved Learning Objectives  Function Templates  Syntax, defining 
 2003 Prentice Hall, Inc. All rights reserved. 1 Chapter 11 - Templates Outline 11.1 Introduction 11.2 Function Templates 11.3 Overloading Function Templates.
OOP Spring 2006 – Recitation 31 Object Oriented Programming Spring 2006 Recitation 3.
 2006 Pearson Education, Inc. All rights reserved. Templates (again)CS-2303, C-Term Templates (again) CS-2303 System Programming Concepts (Slides.
 2006 Pearson Education, Inc. All rights reserved Generics.
CSE 332: C++ Classes From Procedural to Object-oriented Programming Procedural programming –Functions have been the main focus so far Function parameters.
1 CSC241: Object Oriented Programming Lecture No 07.
Review of C++ Programming Part II Sheng-Fang Huang.
CSE 332: C++ templates and generic programming I Motivation for Generic Programming in C++ We’ve looked at procedural programming –Reuse of code by packaging.
CSE 332: C++ Algorithms II From Last Time: Search with Generic Iterators Third generalization: separate iterator type parameter We arrive at the find algorithm.
CSIS 123A Lecture 12 Templates. Introduction  C++ templates  Allow very ‘general’ definitions for functions and classes  Type names are ‘parameters’
CSE 425: Object-Oriented Programming II Implementation of OO Languages Efficient use of instructions and program storage –E.g., a C++ object is stored.
CSE 332: C++ templates This Week C++ Templates –Another form of polymorphism (interface based) –Let you plug different types into reusable code Assigned.
C++ How to Program, 8/e © by Pearson Education, Inc. All Rights Reserved. Note: C How to Program, Chapter 22 is a copy of C++ How to Program Chapter.
Data Structures Using C++ 2E
The Rest of the Story.  Constructors  Compiler-generated  The Initializer List  Copy Constructors  Single-arg (conversion ctors)  The Assignment.
CS212: Object Oriented Analysis and Design Lecture 6: Friends, Constructor and destructors.
CSE 332: C++ Type Programming: Associated Types, Typedefs and Traits A General Look at Type Programming in C++ Associated types (the idea) –Let you associate.
CNS  Executive Overview  Template Parameters  Function Template Issues 2 CNS Templates.
Defining New Types Lecture 21 Hartmut Kaiser
Templates An introduction. Simple Template Functions template T max(T x, T y) { if (x > y) { return x; } else { return y; } } int main(void) { int x =
1 C++ Syntax and Semantics, and the Program Development Process.
Programming Languages by Ravi Sethi Chapter 6: Groupings of Data and Operations.
GPCE'04, Vancouver 1 Towards a General Template Introspection Library in C++ István Zólyomi, Zoltán Porkoláb Department of Programming Languages and Compilers.
Copyright © Curt Hill Generic Classes Template Classes or Container Classes.
Templates ©Bruce M. Reynolds & Cliff Green1 C++ Programming Certificate University of Washington Cliff Green.
CS212: Object Oriented Analysis and Design Lecture 9: Function Overloading in C++
Classes In C++ 1. What is a class Can make a new type in C++ by declaring a class. A class is an expanded concept of a data structure: instead of holding.
More About Objects and Methods Chapter 5. Outline Programming with Methods Static Methods and Static Variables Designing Methods Overloading Constructors.
C# Classes and Inheritance CNS 3260 C#.NET Software Development.
Nirmalya Roy School of Electrical Engineering and Computer Science Washington State University Cpt S 122 – Data Structures Templates.
C++ How to Program, 9/e © by Pearson Education, Inc. All Rights Reserved.
Chapter 7 Templates. Objectives Introduction Function Templates Class Templates Standard Template Library.
CPSC 252 The Big Three Page 1 The “Big Three” Every class that has data members pointing to dynamically allocated memory must implement these three methods:
1 Chapter 2 C++ Syntax and Semantics, and the Program Development Process.
Chapter 10: Classes and Data Abstraction. Objectives In this chapter, you will: Learn about classes Learn about private, protected, and public members.
Operator Overloading. Binary operators Unary operators Conversion Operators –Proxy Classes bitset example Special operators –Indexing –Pre-post increment/decrement.
C++ Programming: From Problem Analysis to Program Design, Third Edition Chapter 15: Overloading and Templates.
CSE 332: C++ Statements C++ Statements In C++ statements are basic units of execution –Each ends with ; (can use expressions to compute values) –Statements.
Inheritance Initialization & Destruction of Derived Objects Protected Members Non-public Inheritance Virtual Function Implementation Virtual Destructors.
1 Becoming More Effective with C++ … Day Two Stanley B. Lippman
Programming Fundamentals. Topics to be covered Today Recursion Inline Functions Scope and Storage Class A simple class Constructor Destructor.
1 Classes II Chapter 7 2 Introduction Continued study of –classes –data abstraction Prepare for operator overloading in next chapter Work with strings.
Template Lecture 11 Course Name: High Level Programming Language Year : 2010.
Overview of C++ Polymorphism
Chapter 10: Classes and Data Abstraction. Classes Object-oriented design (OOD): a problem solving methodology Objects: components of a solution Class:
1 Chapter 15-1 Pointers, Dynamic Data, and Reference Types Dale/Weems.
Chapter 18 Introduction to Custom Templates C++ How to Program, 9/e ©2016 by Pearson Education, Inc., Hoboken, NJ. All Rights Reserved. Instructor Note:
Chapter 16 Templates Copyright © 2008 Pearson Addison-Wesley. All rights reserved.
CSE 332: C++ templates and generic programming II Review: Concepts and Models Templates impose requirements on type parameters –Types that are plugged.
 Binary operators  Unary operators  Conversion Operators  Proxy Classes (simulating a reference) ▪ bitset example  Special operators  Indexing 
CS 342: C++ Overloading Copyright © 2004 Dept. of Computer Science and Engineering, Washington University Overview of C++ Overloading Overloading occurs.
 2006 Pearson Education, Inc. All rights reserved Templates.
CSE 332: C++ Overloading Overview of C++ Overloading Overloading occurs when the same operator or function name is used with different signatures Both.
Templates 3 Templates and type parameters The basic idea templates is simple: we can make code depend on parameters, so that it can be used in different.
CNS  Template Specialization  Member Templates  Template Metaprogramming  Template Idioms  Templates and Friends 2 CNS Templates.
CSE 332: C++ Exceptions Motivation for C++ Exceptions Void Number:: operator/= (const double denom) { if (denom == 0.0) { // what to do here? } m_value.
Copyright © 2002 Pearson Education, Inc. Slide 1.
Copyright © 2002 Pearson Education, Inc. Slide 1.
Programming with ANSI C ++
C++ Templates.
Friend Class Friend Class A friend class can access private and protected members of other class in which it is declared as friend. It is sometimes useful.
Abstraction: Generic Programming, pt. 2
Overview of C++ Polymorphism
Java Programming Language
Lab4 problems More about templates Some STL
四時讀書樂 (春) ~ 翁森 山光照檻水繞廊,舞雩歸詠春風香。 好鳥枝頭亦朋友,落花水面皆文章。 蹉跎莫遣韶光老,人生唯有讀書好。
Presentation transcript:

Understanding C++ Templates CNS 3370 Copyright 2006, Fresh Sources, Inc.

Agenda Executive Overview Template Parameters Function Template Issues Template Specialization Member Templates Template Idioms

Executive Overview Generic Programming Compile-time Code Generation Implicit Interfaces Template Terminology

Generic Programming Writing type-transparent code –Not a new idea –Object mega-hierarchy; void* in C C++ prefers type-safe generics –Based on innovations from ML and Ada –Statically-checked –Allows efficient use of built-in types

Compile-time Code Generation Separate code versions are automatically generated On-demand code generation –Implicit and explicit Compile-time programming –Selective code generation Potential for ODR Violations –Smart linkers solve that Potential for Code Bloat template class Stack { T* data; size_t count; public: void push(const T& t); // etc. }; // Explicit instantiation Stack s;

What Gets Generated? If using a function template, the requested function is instantiated If using a class template, the requested version of the class definition is instantiated –For the compiler’s use (just like regular classes) –But only the member functions actually used are generated Usually :-)

Instantiation On Demand class X { public: void f() {} }; class Y { public: void g() {} }; template class Z { T t; public: void a() { t.f(); } void b() { t.g(); } }; int main() { Z zx; zx.a(); // Z ::b() not attempted Z zy; zy.b(); // Z ::a() not attempted } Question: When are the names f and g looked up by the compiler?

Where Should Template Code Reside? Totally in header files The template code must be available during instantiation –This is the Inclusion Model of template compilation

Implicit Interfaces aka “Duck Typing” –If it quacks like a duck, … (fits the bill :-) The status quo for dynamically-typed languages –Perl, Python, PHP, Ruby… C++ statically verifies that generic types support required operations template const T& min(const T& a, const T& b) { return (a < b) ? a : b; } … min(i, j) // T deduced

Template Terminology A First Look Template Parameter –Names inside <>’s after the template keyword ( ) Template Argument –Names inside <>’s in a specialization ( ) Template Specialization –What results when you give a template some arguments (a class, function, or another template) (Stack ) –A specialization may or may not be an instantiation Instantiation –The class or function generated for a given complete set of template arguments –An instantiation is always a specialization

Template Executive Summary Templates are instructions for compile-time code generation They enable type-safe, type-transparent code Template parameters constitute implicit interfaces –“Duck Typing” –“Compile-time polymorphism” Classic OOP uses explicit interfaces and runtime polymorphism –Templates and OOP complement each other

Sample Programs Template version of Programs 2 (Pool) and 3 (Bits)

Template Parameters 3 Kinds… Type parameters –the most common (vector ) Non-type –compile-time integral values (bitset, for example) Templates –“template template parameters”

Type Parameters The original motivation for templates –“Generic Programming” Container logic is independent of its containee’s type Containers of “T”: –template // T is a type parm class vector { T* data; … }; –vector v;// int is a type argument

Non-type Template Parameters Must be compile-time constant values –usually integral expressions –can also be addresses of static objects or functions –should probably be called “value parameters” Often used to place member arrays on the runtime stack –like with bitset b; –see next slide Often used in Template Metaprogramming –compile-time expressions governing code generation

Using std::bitset

Bitset Implementation (from STLPort) template struct _Base_bitset { typedef unsigned long _WordT; _WordT _M_w[_Nw]; // Can go on stack... }; template class bitset : public _Base_bitset {... };

Default Template Arguments Like default function arguments –if missing, the defaults are supplied –only allowed in class templates template class FixedStack { T data[N]; … }; FixedStack<> s1; // = FixedStack s2; // =

The vector Container Declaration template > class vector; Note how the second parameter uses the first Note the space between the ‘>’s –Will be fixed in C++0x

Template Template Parameters Templates are not types! –They are instructions for generating types –“Meta-types”, if you will If you plan on using a template parameter itself as a template, the compiler needs to know –otherwise it won’t let you do template things with it –You usually don’t use a name for its parameters Examples follow

// A simple, expandable sequence (like vector) template class Array { enum { INIT = 10 }; T* data; size_t capacity; size_t count; public: Array() { count = 0; data = new T[capacity = INIT]; } ~Array() { delete [] data; } void push_back(const T& t) {...} void pop_back() {...} T* begin() { return data; } T* end() { return data + count; } }; A Simple Expandable Sequence (will be used as a template argument)

template class Seq> class Container { Seq seq; public: void append(const T& t) { seq.push_back(t); } T* begin() { return seq.begin(); } T* end() { return seq.end(); } }; int main() { Container container; // Pass template container.append(1); container.append(2); int* p = container.begin(); while(p != container.end()) cout << *p++ << endl; } Passing Array as a template argument

Function Template Issues Type Deduction of Arguments Function template overloading Partial Ordering of Function Templates

Type Deduction in Function Templates Under most circumstances, the compiler deduces type parameters from the arguments in the call –the corresponding specialization is instantiated automatically You can use a fully-qualified call syntax if you want to: int x = min (a, b); // vs. min(a, b); Sometimes you have to: –when the arguments are different types ( min(1.0, 2) ) –when the template argument is a return type, and therefore cannot be deduced by the arguments –Example on next two slides

String Conversion Function Templates // StringConv.h #include template T fromString(const std::string& s) { std::istringstream is(s); T t; is >> t; return t; } template std::string toString(const T& t) { std::ostringstream s; s << t; return s.str(); }

#include #include "StringConv.h" using namespace std; int main() { // Implicit Type Deduction int i = 1234; cout << "i == \"" << toString(i) << "\"" << endl; float x = ; cout << "x == \"" << toString(x) << "\"" << endl; complex c(1.0, 2.0); cout << "c == \"" << toString(c) << "\"" << endl; cout << endl; // Explicit Function Template Specialization i = fromString (string("1234")); cout << "i == " << i << endl; x = fromString (string("567.89")); cout << "x == " << x << endl; c = fromString >(string("(1.0,2.0)")); cout << "c == " << c << endl; }

Another Example implicit_cast Makes a legal implicit conversion visible in code Must specify return type (U) –Can’t be deduced (s = implicit_cast (x);) Can omit T –But only because we put it last in the parameter list –Couldn’t say implicit_cast template U implicit_cast(const T& t) { return t; }

Function Template Overloading You can define multiple functions and function templates with the same name The “best match” will be used –A regular function is preferred over a template All other things being equal –Can force using the template with “<>” You can also overload a function template by having a different number of template parameters

template const T& min(const T& a, const T& b) { return (a < b) ? a : b; } const char* min(const char* a, const char* b) { return (strcmp(a, b) < 0) ? a : b; } double min(double x, double y) { return (x < y) ? x : y; } int main() { const char *s2 = "say \"Ni-!\"", *s1 = "knights who"; cout << min(1, 2) << endl; // 1: 1 (template) cout << min(1.0, 2.0) << endl; // 2: 1 (double) cout << min(1, 2.0) << endl; // 3: 1 (double) cout << min(s1, s2) << endl; // 4: "knights who" // (const char*) cout (s1, s2) << endl; // 5: say "Ni-!" // (template) }

template void f(T) { cout << "T" << endl; } template void f(T*) { cout << "T*" << endl; } template void f(const T*) { cout << "const T*" << endl; } int main() { f(0); // T int i = 0; f(&i); // T* const int j = 0; f(&j); // const T* } Partial Ordering of Function Templates

Things to Remember… About Function Templates Arguments that can be deduced will be –The rest you must provide –Put those first in the argument list so the others can be left to deduction Standard Conversions do not apply when using unqualified calls to function templates –Arguments must match parameters exactly –Except const/volatile adornments are okay Function Templates can be overloaded –The “best match” is used

Template Specialization A template by nature is a generalization It becomes specialized for a particular use when the actual template arguments are provided A particular instantiation is therefore a specialization But there is another type of “specialization”…

Explicit Specialization What if you want special “one-off” behavior for certain combinations of template arguments? You can provide custom code for such cases –both full or partial specializations for class templates Meaning all or some of the template arguments are specified –the compiler will use your explicit versions instead of what the “primary template” would have instantiated –You are writing the “instantiation” Full specialization uses the template<> syntax

A Full Class Specialization Example // A Primary Template template class Foo { public: void f(); void g(); }; template void Foo ::f() { cout << "f using primary template\n"; } template void Foo ::g() { cout << "g using primary template\n"; }

Full Class Specialization Example // The Full Specialization template<> class Foo { public: void f(); void g(); }; // NOTE: No template keyword here // Also: These go in a.cpp void Foo ::f() { cout << "f using int specialization\n"; } void Foo ::g() { cout << "g using int specialization\n"; }

Full Class Specialization Example int main() { Foo c; Foo i; c.f(); c.g(); i.f(); i.g(); } /* Output: f using primary template g using primary template f using int specialization g using int specialization */

Specializing Selected Member Functions You can do a full specialization of only selected member functions The other member functions come from the primary template (See next slide) Other members can be thus specialized –Statics, Member templates

Selective Member Function Spec. Example // Replaces class full specialization: template<> void Foo ::g() { cout << "g using int specialization\n"; } int main() { Foo c; Foo i; c.f(); c.g(); i.f(); i.g(); } /* Output f using primary template g using primary template f using primary template g using int specialization */

Partial Specialization of Class Templates Can specialize on a subset of template arguments –leaving the rest unspecified –can also specialize on: “pointer-ness” “const-ness” equality vector is actually a partial specialization –It specializes the data type, but leaves the allocator type “open”: template class vector {…}; The “most specialized, most restricted” match is preferred See next slide

template class C { public: void f() { cout << "Primary Template\n"; } }; template class C { public: void f() { cout << "T == int\n"; } }; template class C { public: void f() { cout << "U == double\n"; } }; template class C { public: void f() { cout << "T* used\n"; } }; template class C { public: void f() { cout << "U* used\n"; } };

template class C { public: void f() { cout << "T* and U* used\n"; } }; template class C { public: void f() { cout << "T == U\n"; } }; int main() { C ().f(); // 1: Primary template C ().f(); // 2: T == int C ().f(); // 3: U == double C ().f(); // 4: T == U C ().f(); // 5: T* used [T is float] C ().f(); // 6: U* used [U is float] C ().f(); // 7: T* and U* used [float, int] // The following are ambiguous: // 8: C ().f(); // 9: C ().f(); // 10: C ().f(); // 11: C ().f(); // 12: C ().f(); }

Class Specialization Summary You must define all functions you want clients to have when specializing a class –An alternative is to only specialize selected member functions, leaving the rest to the primary template You can do full or partial specializations of class templates –Don’t specialize function templates Except as described above for member functions Overloading and specialization of function templates don’t mix well

More Points to Ponder About Specialization Template parameters in a specialization don’t have to match the primary template –A total specialization is not a template –A partial specialization is a template But the specialization part (in brackets after the class template name) must match: –template Stack {…}; // 1 type parm –template<> Stack {…}; // 1 type parm –template Stack {…}; // 1 type parm

Question What kind of things can you define as members of a class?

Answer Variables –Data members; either static or non-static Functions –Member functions, either static and non-static Types –Either nested classes or typedefs Templates –“Member Templates” –This is where need for a special use of the template keyword arises

Member Types Nested Classes class bitstring { class bitproxy{ … }; bitproxy operator[](int pos) {…} }; If public, could say: bitstring::bitproxy…

Member Types Nested typedefs template class vector { public: class MyIteratorType {…}; typedef MyIteratorType iterator; … }; Can say: vector ::iterator p = v.begin();

The typename keyword Can use typename instead of class in a template parameter declaration –Some people use class when the expected argument(s) must not be built-in types –And they use typename when anything goes Sometimes, though, you need to help the compiler know that an identifier represents a type In particular, when you want to use a member type from a template parameter

The typename keyword ~ continued ~ Consider the expression T::X –When inside a template with type parameter T The compiler assumes a name qualified by a template type parameter refers to a static member (the parser has to assume something, since T is unknown at first) X is a dependent name If X is a type, use the typename keyword to clue the compiler

// A Print function for standard sequences template > class Seq> void printSeq(Seq & seq) { for(typename Seq ::iterator b = seq.begin(); b != seq.end();) cout << *b++ << endl; } int main() { // Process a vector vector v; v.push_back(1); v.push_back(2); printSeq(v); // Process a list list lst; lst.push_back(3); lst.push_back(4); printSeq(lst); }

iterator_traits An application of partial specialization A technique for endowing naked pointers with what other iterators have: –difference_type –value_type –pointer –reference –iterator_category

Implementing iterator_traits // Primary template template struct iterator_traits { typedef typename Iterator::difference_type difference_type; typedef typename Iterator::value_type value_type; typedef typename Iterator::pointer pointer; typedef typename Iterator::reference reference; typedef typename Iterator::iterator_category iterator_category; }; // Partial specialization for pointer types template struct iterator_traits { typedef ptrdiff_t difference_type; typedef T value_type; typedef T* pointer; typedef T& reference; typedef random_access_iterator_tag iterator_category; };

Using iterator_traits // Print any standard sequence or an array template void print(Iter b, Iter e) { typedef typename iterator_traits ::value_type T; copy(b, e, ostream_iterator (cout, "\n")); }

Member Templates Can also nest template definitions inside a class Inside of a class template, too Very handy for conversion constructors: –template class complex { public: template complex(const complex &); –Inside of STL sequences: template deque(InputIterator first, InputIterator last, const Allocator& = Allocator()); Example on next slide

A Member Class Template template class Outer { public: template class Inner { public: void f(); }; template template void Outer ::Inner ::f() { cout << "Outer == " << typeid(T).name() << endl; cout << "Inner == " << typeid(R).name() << endl; cout << "Full Inner == " << typeid(*this).name() << endl; } int main() { Outer ::Inner inner; inner.f(); }

Output from Previous Example Outer == int Inner == bool Full Inner == Outer ::Inner

Member Function Templates Used in practice more than member class templates Cannot be virtual –vtables are fixed-size (traditional compiler techniques) –Since an arbitrary number of instantiations can occur per program, a fixed vtable won’t do –If you want to solve that problem, be my guest :-)

March 2008Copyright © 2008, Fresh Sources, Inc. 58 A Generic Programming Session Task: Write a class that simulates the composition of an arbitrary number of functions –h(x) = f(g(x)) –comp(x) f 1 (f 2 (…f n (x)…)) Strategy: –Hold the function pointers in some sequence container –Apply them in reverse order, starting with f n (x)

From Specialization to Generalization compose1.cpp (function of double) compose2.cpp (functions of T) compose3.cpp (generalizes sequence type) compose4.cpp (generalizes the callable type) –Using a C++0x feature: function compose5.cpp (deduces T) –Using function::result_type and iterator_traits compose6.cpp (uses std::accumulate) compose7.cpp (deduces iterator type)

Name Classification Names can be nicely dichotomized as: –Unqualified x, f( ) –Qualified (provides scope for lookup context) A::x, x.f( ), p->f( ) Names inside templates are also either: –Dependent They depend on a template parameter: e.g., T::iterator We used typename earlier for dependent types –Non-dependent

Template Name Lookup Issues Dependent names cannot be resolved when the compiler first encounters a template definition The compiler must wait until instantiation time to resolve those issues –When actual template arguments are known Hence, a 2-step template compilation process: –1) Template Definition –2) Template Instantiation

Two-phase Template Compilation Template definition time –The template code is parsed –Obvious syntax errors are caught –Non-dependent names are looked up in the context of the template definition (no waiting needed) Template instantiation time –Dependent names are resolved –Which specialization to use is determined A key motivation for 2-phase lookup Examples follow

What Output Should Display? void f(double) { cout << "f(double)" << endl; } template class X { public: void g() { f(1); } }; void f(int) { cout << "f(int)" << endl; } int main() { X ().g(); }

Answer f( ) is a non-dependent name, so it is looked up when the template code is parsed f(double) is in scope at the time, so the call resolves to that function, not f(int) Some compilers do the wrong thing here –For historical reasons (the rules were decided on late in the game)

A Second Example The underlined calls fail! template class PQV : public vector { Compare comp; public: PQV(Compare cmp = Compare()) : comp(cmp) { make_heap(begin(), end(), comp); } const T& top() const { return front(); } void push(const T& x) { push_back(x); push_heap(begin(), end(), comp); } void pop() { pop_heap(begin(), end(), comp); pop_back(); } };

Dependent Base Classes Not considered during name lookup template class PQV : public vector { Compare comp; public: PQV(Compare cmp = Compare()) : comp(cmp) { make_heap(this->begin(),this->end(), comp); } const T& top() const { return this->front(); } void push(const T& x) { this->push_back(x); push_heap(this->begin(),this->end(), comp); } void pop() { pop_heap(this->begin(),this->end(), comp); this->pop_back(); } };

Moral “x” is not always the same as “this->x”! In tempaltes, anyway

Template Metaprogramming Compile-time Computation! –whoa! Has been proven to be “Turing complete” –means that theoretically, you can do anything at compile time –in practice, it’s used for custom code generation, compile-time assertions, and numerical libraries

Runtime Factorial The compiler can do integer arithmetic –Common in legacy preprocessor operations Compile-time recursion: –Use a primary template for the recursion Uses in turn an instantiation of itself –A specialization stops the recursion at the base case See factorial.cpp

Runtime binary-to-decimal conversion (from David Abrahams) template struct binary { static const unsigned int value = binary ::value << 1 | N%10; }; // A (full/total) specialization to stop the recursion. template<> struct binary { static const unsigned int value = 0; }; int main() { cout ::value << endl; // 5 cout ::value << endl; // 622 cout ::value << endl; // 231 }

Traits A way to store type-specific code for templates –“Compile-time polymorphism” –Use separate template specializations for each type Examples: –std::numeric_limits –std::char_traits

std::numeric_limits template class numeric_limits { public: static const bool is_specialized = false; static T min() throw(); // for float, etc. static T max() throw(); static const int digits = 0; static const int digits10 = 0; static const bool is_signed = false; static const bool is_integer = false; static const bool is_exact = false; static const int radix = 0; static T epsilon() throw();... }; float eps = numeric_limit ::epsilon();

IEEE Traits template struct IEEE_traits {}; template<> struct IEEE_traits { typedef float FType; enum { nbytes = sizeof(float), nbits = nbytes*8, exp_bits = 8, bias = 127 }; template<> struct IEEE_traits { typedef double FType; enum { nbytes = sizeof(double), nbits = nbytes*8, exp_bits = 11, bias = 1023 };

Using IEEE_Traits template bool isinfinity(FType x) { return exponent(x) == IEEE_traits ::bias+1 && fraction(x) == FType(0); } template bool isnan(FType x) { return exponent(x) == IEEE_traits ::bias+1 && fraction(x) != 0; }

Policies Similar to Traits –But the emphasis is on functionality (not data) –Template arguments represent implementation strategies Examples: –STL sequences –Allocators –Alexandrescu Singleton

C++’s Container Adapters Policies in Action queue, stack, priority_queue Implemented with an underlying sequence data structure –vector, deque, or list or roll your own You glue them together at compile time: –queue > –Default storage “policy” is deque

Allocators Recall the definition of std::vector: template > class vector; std::allocator is a memory management policy –It uses new and delete –But you can provide your own custom allocator class A pool allocator, say

Alexandrescu’s Singleton Has 4 template parameters: –The class to “singleton-ize” –Storage Policy –Lifetime Policy –Threading Policy Singleton x; –Defaults to SingleThreaded

Counting Objects Can use a static data member that tracks the object count Constructors increment Destructors decrement

Counting Non-template objects // This is C++ 101: class CountedClass { static int count; public: CountedClass() { ++count; } CountedClass(const CountedClass&) { ++count; } ~CountedClass() { --count; } static int getCount() { return count; } }; int CountedClass::count = 0;

How not to share Counting Code class Counted { static int count; public: Counted() { ++count; } Counted(const Counted&) { ++count; } ~Counted() { --count; } static int getCount() { return count; } }; int Counted::count = 0; // All derived classes share the same count! class CountedClass : public Counted {}; class CountedClass2 : public Counted {};

The Solution We need a separate count for each class to be counted We need to inherit from a different class for each client class Hence, we need to use both inheritance (OOP) and templates (compile-time polymorphism) See next slide

A Template Counter Solution template class Counted { static int count; public: Counted() { ++count; } Counted(const Counted &) { ++count; } ~Counted() { --count; } static int getCount() { return count; } }; template int Counted ::count = 0; // Curious class definitions!!! class CountedClass : public Counted {}; class CountedClass2 : public Counted {};

A Curiously Recurring Template Pattern (CRTP) A class, T, inherits from a template that specializes on T! class T : public X {…}; Only valid if the size of X can be determined independently of T –i.e., during Phase 1

Singleton via CRTP Inherit Singleton-ness Uses Meyers’ static singleton object approach –Since nothing non-static is inherited, the size is known at template definition time Protected constructor, destructor Disables copy/assign See next slide

Singleton via CRTP // Base class – encapsulates singleton-ness template class Singleton { Singleton(const Singleton&); Singleton& operator=(const Singleton&); protected: Singleton() {} virtual ~Singleton() {} public: static T& instance() { static T theInstance; // Meyers' Singleton return theInstance; } };

Making a Class a Singleton // A sample class to be made into a Singleton class MyClass : public Singleton { int x; protected: friend class Singleton ; // to create it MyClass() { x = 0; } public: void setValue(int n) { x = n; } int getValue() const { return x; } }; int main() { MyClass& m = MyClass::instance(); cout << m.getValue() << endl; m.setValue(1); cout << m.getValue() << endl; }

A Simple Class Template How can we add a stream inserter? –ostream& operator(ostream&, const Box &); template class Box { T t; public: Box(const T& theT) : t(theT) {} };

template class Box { T value; public: Box(const T& t) { value = t; } friend ostream& operator &); }; template ostream& operator b) { return os << b.value; } This Won’t Work! Templates Are Different

What’s the Problem? The inserter is not a template –But it uses a template argument (T) –This is a problem since it’s not a member function –Our operator<<( ) must be a template What do we want? –We want a distinct specialization for each T –But it can’t be a member template! That would introduce an independent template parameter Aargh!

Solution 1 There are special rules for friend function templates to class templates –Use angle brackets in the declaration of the friend Can be empty if the function parameters are sufficient to deduce the template arguments –But… the friend must be previously declared An exception to the usual automatic assumption that the function is in the enclosing scope This also requires a forward declaration of Box See Next Slide

Friend Function Templates // Forward declarations template class Box; template ostream& operator<<(ostream&, const Box &); template class Box { T value; public: Box(const T& t) { value = t; } friend ostream& operator (ostream&, const Box &); }; template ostream& operator & b) { return os << b.value; }

template class Box { T value; public: Box(const T& t) { value = t; } friend ostream& operator & b) { return os << b.value; } }; Solution 2

Making New Friends Define body of operator<< in situ –(i.e.,inside of Box) Such an operator<< is not technically a template! –No angle brackets are used A new ordinary function is created for each specialization of Box! –Names must be suitably mangled –Like inner classes in Java

Had Enough? There’s still more to cover! But let’s stop here.