understanding c++ templates cns 3370 copyright 2006, fresh sources, inc

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

Upload: johnathan-terry

Post on 02-Jan-2016

215 views

Category:

Documents


1 download

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<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

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 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

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 (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

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 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();}

Output from Previous Example

Outer == intInner == boolFull 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 :-)

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(); }};

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 << 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

Had Enough?

• There’s still more to cover!

• But let’s stop here.