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 instantiationStack<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 TerminologyA 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
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 parmclass 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
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 (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; }};
A Simple Expandable Sequence(will be used 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;}
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<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 Exampleimplicit_cast
• 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>
template <class U, class T>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<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)}
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*}
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 SpecializationExample
// A Primary Templatetemplate<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 SpecializationExample
// The Full Specializationtemplate<>class Foo<int> {public: void f(); void g();};
// NOTE: No template keyword here// Also: These go in a .cppvoid Foo<int>::f() { cout << "f using int specialization\n";}
void Foo<int>::g() { cout << "g using int specialization\n";}
Full Class SpecializationExample
int main() { Foo<char> c; Foo<int> i; c.f(); c.g(); i.f(); i.g();}
/* Output:f using primary templateg using primary templatef using int specializationg 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(); }
/* Outputf using primary templateg using primary templatef using primary templateg 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 PonderAbout 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
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 TypesNested Classes
class bitstring {
class bitproxy{
…
};
bitproxy operator[](int pos) {…}
};
If public, could say:
bitstring::bitproxy…
Member TypesNested 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 sequencestemplate<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_traitsAn 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 templatetemplate<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 typestemplate<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 arraytemplate<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();}
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 2008 Copyright © 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) f1(f2(…fn(x)…))
• Strategy:– Hold the function pointers in some sequence container
– Apply them in reverse order, starting with fn(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 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 ExampleThe 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 ClassesNot 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(); }};
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 << binary<101>::value << endl; // 5 cout << binary<1001101110>::value << endl; // 622 cout << binary<11100111>::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 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 AdaptersPolicies 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-nesstemplate<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 Singletonclass 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) {}};
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;}
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 declarationstemplate<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;}
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; }};
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