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<typename T> class Stack { T* data; size_t count; public: void push(const T& t); // etc. }; // Explicit instantiation Stack<int> 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<typename T> class Z { T t; public: void a() { t.f(); } void b() { t.g(); } }; int main() { Z<X> zx; zx.a(); // Z<X>::b() not attempted Z<Y> zy; zy.b(); // Z<Y>::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<class T> 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 (<T>) • Template Argument – Names inside <>’s in a specialization (<int>) • Template Specialization – What results when you give a template some arguments (a class, function, or another template) (Stack<int>) – 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<int>) • Non-type – compile-time integral values (bitset<10>, 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<class T> // T is a type parm class vector { T* data; … }; – vector<int> 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<128> b; – see next slide • Often used in Template Metaprogramming – compile-time expressions governing code generation Using std::bitset • <need sample code here> Bitset Implementation (from STLPort) template<size_t _Nw> struct _Base_bitset { typedef unsigned long _WordT; _WordT _M_w[_Nw]; // Can go on stack . . . }; template<size_t _Nb> class bitset : public _Base_bitset<__BITSET_WORDS(_Nb) > { . . . }; Default Template Arguments • Like default function arguments – if missing, the defaults are supplied – only allowed in class templates • template<class T = int, size_t N = 100> class FixedStack { T data[N]; … }; FixedStack<> s1; // = <int, 100> FixedStack<float> s2; // = <float,100> The vector Container Declaration • template<class T, class Allocator = allocator<T> > 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 (will be used as a template argument) // A simple, expandable sequence (like vector) template<class T> 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; } }; Passing Array as a template argument template<class T, template<class> class Seq> class Container { Seq<T> seq; public: void append(const T& t) { seq.push_back(t); } T* begin() { return seq.begin(); } T* end() { return seq.end(); } }; int main() { Container<int, Array> container; // Pass template container.append(1); container.append(2); int* p = container.begin(); while(p != container.end()) cout << *p++ << endl; } 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<int>(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 <string> #include <sstream> template<class T> T fromString(const std::string& s) { std::istringstream is(s); T t; is >> t; return t; } template<class T> std::string toString(const T& t) { std::ostringstream s; s << t; return s.str(); } #include <complex> #include <iostream> #include "StringConv.h" using namespace std; int main() { // Implicit Type Deduction int i = 1234; cout << "i == \"" << toString(i) << "\"" << endl; float x = 567.89; cout << "x == \"" << toString(x) << "\"" << endl; complex<float> c(1.0, 2.0); cout << "c == \"" << toString(c) << "\"" << endl; cout << endl; // Explicit Function Template Specialization i = fromString<int>(string("1234")); cout << "i == " << i << endl; x = fromString<float>(string("567.89")); cout << "x == " << x << endl; c = fromString<complex<float> >(string("(1.0,2.0)")); cout << "c == " << c << endl; } Another Example implicit_cast template <class U, class T> U implicit_cast(const T& t) { return t; } • Makes a legal implicit conversion visible in code • Must specify return type (U) – Can’t be deduced (s = implicit_cast<string>(x);) • Can omit T – But only because we put it last in the parameter list – Couldn’t say implicit_cast<?, string> 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<class T> 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 << min<>(s1, s2) << endl; // 5: say "Ni-!" // (template) } Partial Ordering of Function Templates template<class T> void f(T) { cout << "T" << endl; } template<class T> void f(T*) { cout << "T*" << endl; } template<class T> 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* } 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 T> class Foo { public: void f(); void g(); }; template<class T> void Foo<T>::f() { cout << "f using primary template\n"; } template<class T> void Foo<T>::g() { cout << "g using primary template\n"; } Full Class Specialization Example // The Full Specialization template<> class Foo<int> { public: void f(); void g(); }; // NOTE: No template keyword here // Also: These go in a .cpp void Foo<int>::f() { cout << "f using int specialization\n"; } void Foo<int>::g() { cout << "g using int specialization\n"; } Full Class Specialization Example int main() { Foo<char> c; Foo<int> 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<int>::g() { cout << "g using int specialization\n"; } int main() { Foo<char> c; Foo<int> 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<bool> is actually a partial specialization – It specializes the data type, but leaves the allocator type “open”: template<class Allocator> class vector<bool, Allocator> {…}; • The “most specialized, most restricted” match is preferred • See next slide template<class T, class U> class C { public: void f() { cout << "Primary Template\n"; } }; template<class U> class C<int, U> { public: void f() { cout << "T == int\n"; } }; template<class T> class C<T, double> { public: void f() { cout << "U == double\n"; } }; template<class T, class U> class C<T*, U> { public: void f() { cout << "T* used\n"; } }; template<class T, class U> class C<T, U*> { public: void f() { cout << "U* used\n"; } }; template<class T, class U> class C<T*, U*> { public: void f() { cout << "T* and U* used\n"; } }; template<class T> class C<T, T> { public: void f() { cout << "T == U\n"; } }; int main() { C<float, int>().f(); // 1: Primary template C<int, float>().f(); // 2: T == int C<float, double>().f(); // 3: U == double C<float, float>().f(); // 4: T == U C<float*, float>().f(); // 5: T* used [T is float] C<float, float*>().f(); // 6: U* used [U is float] C<float*, int*>().f(); // 7: T* and U* used [float, int] // The following are ambiguous: // 8: C<int, int>().f(); // 9: C<double, double>().f(); // 10: C<float*, float*>().f(); // 11: C<int, int*>().f(); // 12: C<int*, int*>().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<class T> Stack<T*> {…}; // 1 type parm – template<> Stack<char*> {…}; // 1 type parm – template<class R, class A> Stack<R (*)(A)> {…}; // 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 T,…> class vector { public: class MyIteratorType {…}; typedef MyIteratorType iterator; … }; Can say: vector<int>::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<typename T, template<typename U, typename = allocator<U> > class Seq> void printSeq(Seq<T>& seq) { for(typename Seq<T>::iterator b = seq.begin(); b != seq.end();) cout << *b++ << endl; } int main() { // Process a vector vector<int> v; v.push_back(1); v.push_back(2); printSeq(v); // Process a list list<int> 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<class Iterator> 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<class T> struct iterator_traits<T*> { 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<class Iter> void print(Iter b, Iter e) { typedef typename iterator_traits<Iter>::value_type T; copy(b, e, ostream_iterator<T>(cout, "\n")); } Member Templates • Can also nest template definitions inside a class • Inside of a class template, too • Very handy for conversion constructors: – template<typename T> class complex { public: template<class X> complex(const complex<X>&); – Inside of STL sequences: template <class InputIterator> deque(InputIterator first, InputIterator last, const Allocator& = Allocator()); • Example on next slide A Member Class Template template<class T> class Outer { public: template<class R> class Inner { public: void f(); }; }; template<class T> template<class R> void Outer<T>::Inner<R>::f() { cout << "Outer == " << typeid(T).name() << endl; cout << "Inner == " << typeid(R).name() << endl; cout << "Full Inner == " << typeid(*this).name() << endl; } int main() { Outer<int>::Inner<bool> inner; inner.f(); } Output from Previous Example Outer == int Inner == bool Full Inner == Outer<int>::Inner<bool> 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 :-) 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) f1(f2(…fn(x)…)) • Strategy: – Hold the function pointers in some sequence container – Apply them in reverse order, starting with fn(x) March 2008 Copyright © 2008, Fresh Sources, Inc. 58 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 T> class X { public: void g() { f(1); } }; void f(int) { cout << "f(int)" << endl; } int main() { X<int>().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 T, class Compare> class PQV : public vector<T> { 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 T, class Compare> class PQV : public vector<T> { 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<unsigned long N> struct binary { static const unsigned int value = binary<N/10>::value << 1 | N%10; }; // A (full/total) specialization to stop the recursion. template<> struct binary<0> { static const unsigned int value = 0; }; int main() cout << cout << cout << } { binary<101>::value << endl; binary<1001101110>::value << endl; binary<11100111>::value << endl; // 5 // 622 // 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 T> 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<float>::epsilon(); IEEE Traits template<typename T> struct IEEE_traits {}; template<> struct IEEE_traits<float> { typedef float FType; enum { nbytes = sizeof(float), nbits = nbytes*8, exp_bits = 8, bias = 127 }; }; template<> struct IEEE_traits<double> { typedef double FType; enum { nbytes = sizeof(double), nbits = nbytes*8, exp_bits = 11, bias = 1023 }; }; Using IEEE_Traits template<typename FType> bool isinfinity(FType x) { return exponent(x) == IEEE_traits<FType>::bias+1 && fraction(x) == FType(0); } template<typename FType> bool isnan(FType x) { return exponent(x) == IEEE_traits<FType>::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<int, list<int> > – Default storage “policy” is deque<T> Allocators • Recall the definition of std::vector: template<class T, class Allocator = allocator<T> > class vector; • std::allocator<T> 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<MyClass,CreateStatic,NoDestroy> 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 T> class Counted { static int count; public: Counted() { ++count; } Counted(const Counted<T>&) { ++count; } ~Counted() { --count; } static int getCount() { return count; } }; template<class T> int Counted<T>::count = 0; // Curious class definitions!!! class CountedClass : public Counted<CountedClass> {}; class CountedClass2 : public Counted<CountedClass2> {}; A Curiously Recurring Template Pattern (CRTP) • A class, T, inherits from a template that specializes on T! • class T : public X<T> {…}; • Only valid if the size of X<T> 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 T> 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<MyClass> { int x; protected: friend class Singleton<MyClass>; // 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<T>&); template<class T> class Box { T t; public: Box(const T& theT) : t(theT) {} }; This Won’t Work! Templates Are Different template<typename T> class Box { T value; public: Box(const T& t) { value = t; } friend ostream& operator<<(ostream&, const Box<T>&); }; template<typename T> ostream& operator<<(ostream os, const Box<T> b) { return os << b.value; } 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 T> class Box; template<class T> ostream& operator<<(ostream&, const Box<T>&); template<class T> class Box { T value; public: Box(const T& t) { value = t; } friend ostream& operator<< <>(ostream&, const Box<T>&); }; template<class T> ostream& operator<<(ostream& os, const Box<T>& b) { return os << b.value; } Solution 2 template<typename T> class Box { T value; public: Box(const T& t) { value = t; } friend ostream& operator<<(ostream& os, const Box<T>& b) { return os << b.value; } }; 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.