- Contents - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - A - B - Index -

5: Templates in depth

The C++ template facility goes far beyond simple “containers of T.” Although the original motivation was to enable type-safe, generic containers, in modern C++, templates are also used to generate custom code and to optimize program execution through compile-time programming constructs. Comment

In this chapter we offer a practical look at the power (and pitfalls) of programming with templates in modern C++. For a more complete analysis of template-related language issues and “gotchas,” we recommend the superb book by David Vandevoorde and Nico Josuttis.[49]ÂŽÂŽ Comment

Template parameters

As we illustrated in Volume 1, templates come in two flavors: function templates and class templates. Both are wholly characterized by their parameters. Each template parameter itself can represent one of the following categories of arguments:

1.       Types (either built-in or user-defined)

2.      Compile-time constant values (for example, integers, and pointers and references to static entities; often referred to as non-type parameters)

3.      Other templates

The examples in Volume 1 all fall into the first category and are the most common. The canonical example for simple container-like templates nowadays seems to be a Stack class. Being a container, a Stack object is not concerned with the type of object it stores; the logic of holding objects is independent of the type of objects being held. For this reason you can use a type parameter to represent the contained type: Comment

template<class T>

class Stack {

ÂŽÂŽ  T* data;

ÂŽÂŽ  size_t count;

public:

ÂŽÂŽ  void push(const T& t);

ÂŽÂŽ  // etc.

};

 

You provide the actual type to be used for a particular Stack instance by means of an argument for the parameter T:

Stack<int> myStack;ÂŽÂŽ  // A Stack of ints

 

The compiler then provides an int-version of Stack by substituting int for T and generating the corresponding code. The name of the class instance generated from the template in this case is Stack<int>. Comment

Non-type template parameters

It is also possible to provide a non-type template parameter, as long as it represents an integral value that is known at compile time. You can make a fixed-size Stack, for instance, by specifying a non-type parameter to be used as the dimension for the underlying array, as follows. Comment

template<class T, size_t N>

class Stack {

  T data[N];   // Fixed capacity is N

  size_t count;

public:

  void push(const T& t);

  // etc.

};

 

You must provide a compile-time constant value for the parameter N when you request an instance of this template, such as

Stack<int, 100> myFixedStack;

 

Because the value of N is known at compile time, the underlying array (data) can be placed on the runtime stack instead of on the free store, which can improve runtime performance by avoiding the overhead associated with dynamic memory allocation. Following the pattern mentioned earlier, the name of the class above is Stack<int, 100>. This means that each distinct value of N results in a unique class type. For example, Stack<int, 99> is a distinct class from Stack<int, 100>. Comment

The bitset class template, discussed in detail in Chapter 7, is the only class in the standard C++ library that uses a non-type template parameter, which happens to specify the number of bits the bitset object can hold. The following random number generator example uses a bitset to track numbers so all the numbers in its range are returned in random order without repetition before starting over. This example also overloads operator( ) to produce a familiar function-call syntax. Comment

//: C05:Urand.h

//{-bor}

// Unique randomizer

#ifndef URAND_H

#define URAND_H

#include <bitset>

#include <cstddef>

#include <cstdlib>

#include <ctime>

using std::size_t;

using std::bitset;

 

template<size_t UpperBound>

class Urand {

  bitset<UpperBound> used;

public:

  Urand() {

      srand(time(0));   // randomize

  }

  size_t operator()(); // The "generator" function

};

 

template<size_t UpperBound>

inline size_t Urand<UpperBound>::operator()() {

  if(used.count() == UpperBound)

      used.reset();  // start over (clear bitset)

  size_t newval;

  while(used[newval = rand() % UpperBound])

      ; // Until unique value is found

  used[newval] = true;

  return newval;

}

#endif // URAND_H ///:~

 

The uniqueness of Urand is produced by tracking with a bitset all the numbers possible in the random space (the upper bound is set with the template argument) and by recording each number as itÂ’s used by setting the corresponding position bit in used. When the numbers are all used up, the bitset is cleared to start over. HereÂ’s a simple driver that illustrates how to use a Urand object: Comment

//: C05:UrandTest.cpp

//{-bor}

#include <iostream>

#include "Urand.h"

using namespace std;

 

int main() {

  Urand<10> u;

  for(int i = 0; i < 20; ++i)

      cout << u() << ' ';

} ///:~

 

As we explain later in this chapter, non-type template arguments are also important in the optimization of numeric computations. Comment

Default template arguments

You can provide default arguments for template parameters in class templates. (They are not allowed in function templates.) As with default function arguments, they should only be defined once, the first time a template declaration or definition is seen by the compiler; and once you introduce a default argument, all the subsequent template parameters must also have defaults. To make the fixed-size Stack template shown earlier a little friendlier, for example, you can add a default argument like this: Comment

template<class T, size_t N = 100>

class Stack {

  T data[N];   // Fixed capacity is N

  size_t count;

public:

  void push(const T& t);

  // etc.

};

 

Now, if you omit the second template argument when declaring a Stack object, the value for N will default to 100. Comment

You can choose to provide defaults for all arguments, but you must use an empty set of brackets when declaring an instance so that the compiler knows that a class template is involved. HereÂ’s how:

template<class T = int, size_t N = 100>   // Both defaulted

class Stack {

  T data[N];   // Fixed capacity is N

  size_t count;

public:

  void push(const T& t);

  // etc.

};

 

Stack<> myStack;   // Same as Stack<int, 100> Comment

 

Default arguments are used heavily in the standard C++ library. The vector class template, for instance, is declared as follows:

template <class T, class Allocator = allocator<T> >

class vector;

 

Note the space between the last two right angle bracket characters. This prevents the compiler from interpreting those two characters (>>) as the right-shift operator.

This declaration reveals that vector actually takes two arguments: the type of the contained objects it holds, and a type that represents the allocator used by the vector. (We talk more about allocators in Chapter 7.) Whenever you omit the second argument, the standard allocator template is used, parameterized by the first template parameter. This declaration also shows that you can use template parameters in other, subsequent template parameters, as T is used here. Comment

Although you cannot use default template arguments in function templates, you can use template parameters as default arguments to normal functions. The following function template adds the elements in a sequence. Comment

//: C05:FuncDef.cpp

#include <iostream>

using namespace std;

 

template<class T>

T sum(T* b, T* e, T init = T()) {

  while(b != e)

      init += *b++;

  return init;

}

 

int main() {

  int a[] = {1,2,3};

  cout << sum(a, a+sizeof a / sizeof a[0]) << endl; // 6

} ///:~

 

The third argument to sum( ) is the initial value for the accumulation of the elements. Since we omitted it, this argument defaults to T( ), which in the case of int and other built-in types invokes a pseudo-constructor that performs zero-initialization. Comment

Template template parameters

The third type of parameter a template can accept is another class template. This may sound strange, since templates are types, and type parameters are already allowed, but if you are going to use a template type parameter as a template in your code, the compiler needs to know that the parameter is a template in the first place. The following example illustrates a template template parameter. Comment

//: C05:TempTemp.cpp

// Illustrates a template template parameter

#include <cstddef>

#include <iostream>

using namespace std;

 

template<class T>

class Array { // A simple, expandable sequence

  enum {INIT = 10};

  T *data;

  size_t capacity;

  size_t count;

public:

  Array() {

      count = 0;

      data = new T[capacity = INIT];

  }

  void push_back(const T& t) {

      if(count == capacity) {

          // Grow underlying array

          size_t newCap = 2*capacity;

          T* newData = new T[newCap];

          for (size_t i = 0; i < count; ++i)

              newData[i] = data[i];

          delete data;

          data = newData;

          capacity = newCap;

      }

      data[count++] = t;

  }

  void pop_back() {

      if(count > 0)

          --count;

  }

  T* begin() {

      return data;

  }

  T* end() {

      return data + count;

  }

};

 

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

  theData.append(1);

  theData.append(2);

  int* p = theData.begin();

  while(p != theData.end())

      cout << *p++ << endl;

} ///:~

 

The Array class template is a trivial sequence container. The Container template takes two parameters: the type of the objects it is to hold, and a sequence data structure to do the holding. The following line in the implementation of the Container class requires that we inform the compiler that Seq is a template: Comment

  Seq<T> seq;

 

If we hadnÂ’t declared Seq to be a template template parameter, the compiler would complain here that Seq is not a template, since weÂ’re using it as such. In main( ) a Container is instantiated to use an Array to hold integers, so Seq stands for Array in this example. Comment

Note that it is not necessary in this case to name the parameter for Seq inside ContainerÂ’s declaration. The line in question is:

template<class T, template<class> class Seq>

 

Although we could have written

template<class T, template<class U> class Seq>

 

the parameter U is not needed anywhere. All that matters is that Seq is a class template that takes a single type parameter. This is analogous to omitting the names of function parameters when theyÂ’re not needed, such as when you overload the post-increment operator: Comment

T operator++(int);

 

The int here is merely a placeholder and therefore needs no name.

The following program uses a fixed-size array, which has an extra template parameter representing the array dimension:

//: C05:TempTemp2.cpp

// A multi-variate template template parameter

#include <cstddef>

#include <iostream>

using namespace std;

 

template<class T, size_t N>

class Array {

  T data[N];

  size_t count;

public:

  Array() { count = 0; }

  void push_back(const T& t) {

      if(count < N)

          data[count++] = t;

  }

  void pop_back() {

      if(count > 0)

          --count;

  }

  T* begin() { return data; }

  T* end() { return data + count; }

};

 

template<class T,size_t N,template<class,size_t> class Seq>

class Container {

  Seq<T,N> seq;

public:

  void append(const T& t) { seq.push_back(t); }

  T* begin() { return seq.begin(); }

  T* end() { return seq.end(); }

};

 

int main() {

  const size_t N = 10;

  Container<int, N, Array> theData;

  theData.append(1);

  theData.append(2);

  int* p = theData.begin();

  while(p != theData.end())

      cout << *p++ << endl;

} ///:~

 

Once again, parameter names are not needed in the declaration of Seq inside ContainerÂ’s declaration, but we need two parameters to declare the data member seq, hence the appearance of the non-type parameter N at the top level. Comment

Combining default arguments with template template parameters is slightly more problematic. The When the compiler looks at the inner parameters of a template template parameter, default arguments are not considered, so you have to repeat the defaults in order to get an exact match. The following example uses a default argument for the fixed-size Array template and shows how to accommodate this quirk in the language. Comment

//: C05:TempTemp3.cpp

//{-bor}

//{-msc}

// Combining template template parameters and

// default arguments

#include <cstddef>

#include <iostream>

using namespace std;

 

template<class T, size_t N = 10>   // A default argument

class Array {

  T data[N];

  size_t count;

public:

  Array() { count = 0; }

  void push_back(const T& t) {

      if(count < N)

          data[count++] = t;

  }

  void pop_back() {

      if(count > 0)

          --count;

  }

  T* begin() { return data; }

  T* end() { return data + count; }

};

 

template<class T, template<class, size_t = 10> class Seq>

class Container {

  Seq<T> seq;   // Default used

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

  theData.append(1);

  theData.append(2);

  int* p = theData.begin();

  while(p != theData.end())

      cout << *p++ << endl;

} ///:~

 

It is necessary to include the default dimension of 10 in the line:

template<class T, template<class, size_t = 10> class Seq>

 

Both the definition of seq in Container and theData in main( ) use the default. The only way to use something other than the default value is as the previous program (TempTemp2.cpp) illustrated. This is the only exception to the rule stated earlier that default arguments should appear only once in a compilation unit. Comment

Since the standard sequence containers (vector, list, and deque, discussed in depth in Chapter 7) have a default allocator argument, the technique shown above is helpful should you ever want to pass one of these sequences as a template parameter. The following program passes a vector and then a list to two instances of Container. Comment

//: C05:TempTemp4.cpp

//{-bor}

//{-msc}

// Passes standard sequences as template arguments

#include <iostream>

#include <list>

#include <memory>   // Declares allocator<T>

#include <vector>

using namespace std;

 

template<class T, template<class U, class = allocator<U> >

                                     class Seq>

class Container {

  Seq<T> seq; // Default of allocator<T> applied implicitly

public:

  void push_back(const T& t) { seq.push_back(t); }

  typename Seq<T>::iterator begin() { return seq.begin(); }

  typename Seq<T>::iterator end() { return seq.end(); }

};

 

int main() {

  // Use a vector

  Container<int, vector> theData;

  theData.push_back(1);

  theData.push_back(2);

  for(vector<int>::iterator p = theData.begin();

          p != theData.end(); ++p) {

      cout << *p << endl;

  }

  // Use a list

  Container<int, list> theOtherData;

  theOtherData.push_back(3);

  theOtherData.push_back(4);

  for(list<int>::iterator p2 = theOtherData.begin();

          p2 != theOtherData.end(); ++p2) {

      cout << *p2 << endl;

  }

} ///:~

 

In this case we name the first parameter of the inner template Seq (with the name U), because the allocators in the standard sequences must themselves be parameterized with the same type as the contained objects in the sequence. Also, since the default allocator parameter is known, we can omit it in the subsequent references to Seq<T>, as we did in the previous program. To fully explain this example, however, we have to discuss the semantics of the typename keyword. Comment

The typename keyword

Consider the following: Comment

//: C05:TypenamedID.cpp

//{-bor}

// Uses 'typename' as a prefix for nested types

 

template<class T> class X {

  // Without typename, you should get an error:

  typename T::id i;

public:

  void f() { i.g(); }

};

 

class Y {

public:

  class id {

  public:

      void g() {}

  };

};

 

int main() {

  X<Y> xy;

  xy.f();

} ///:~

 

The template definition assumes that the class T that you hand it must have a nested identifier of some kind called id. But id could also be a static data member of T, in which case you can perform operations on id directly, but you can’t “create an object” of “the type id.” Comment

However, thatÂ’s exactly what is happening here: the identifier id is being treated as if it were actually a nested type inside T. In the case of class Y, id is in fact a nested type, but (without the typename keyword) the compiler canÂ’t know that when itÂ’s compiling X. Comment

If the compiler has the option of treating an identifier as a type or as something other than a type when it sees an identifier in a template, it will assume that the identifier refers to something other than a type. That is, it will assume that the identifier refers to an object (including variables of primitive types), an enumeration, or something similar. However, it will not–cannot–just assume that it is a type. Comment

Because the default behavior of the compiler is to assume that a name that fits the above two points is not a type, you must use typename for nested names (except in constructor initializer lists, where it is neither needed nor allowed). In the above example, when the compiler sees template T::id, it knows (because of the typename keyword) that id refers to a nested type and thus it can create an object of that type. Comment

The short version of the rule is: if a type referred to inside template code is qualified by a template type parameter, it should be preceded by the typename keyword, unless it appears in a base class specification or initializer list in the same scope (in which case you must not).

All the above explains the use of the typename keyword in the program TempTemp4.cpp. Without it, the compiler would assume that the expression Seq<T>::iterator is not a type, but we were using it to define the return type of the begin( ) and end( ) member functions. Comment

The following example, which defines a function template that can print any standard C++ sequence, shows a similar use of typename.

//: C05:PrintSeq.cpp

//{-msc}

// A print function for standard C++ sequences

#include <iostream>

#include <list>

#include <memory>

#include <vector>

using namespace std;

 

template<class T, template<class U, class = 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);

} ///:~

 

Once again, without the typename keyword the compiler will interpret iterator as a static data member of Seq<T>, which is a syntax error, since a type is required. Comment

Typedef-ing a typename

ItÂ’s important not to assume that the typename keyword creates a new type name. It doesnÂ’t. Its purpose is to inform the compiler that the qualified identifier is to be interpreted as a type. A line that reads: Comment

typename Seq<T>::iterator It;

 

causes a variable named It to be declared of type Seq<T>::iterator. If you mean to create a new type name, you should use typedef, as usual, as in: Comment

typedef typename Seq<It>::iterator It;

 

Using typename instead of class

Another role of the typename keyword is to provide you the option of using typename instead of class in the template argument list of a template definition. To some, this produces clearer code (your mileage may vary): Comment

//: C05:UsingTypename.cpp

// Using 'typename' in the template argument list

 

template<typename T> class X { };

 

int main() {

  X<int> x;

} ///:~

 

You probably wonÂ’t see a great deal of code that uses typename in this fashion, since the keyword was added to the language a relatively long time after templates were introduced. Comment

Using the template keyword as a hint

Just as the typename keyword helps the compiler in situations in which a type identifier is not expected, there is also a potential difficulty with tokens that are not identifiers, such as the < and > characters; sometimes they represent the less-than or greater-than symbols, and sometimes they delimit template parameter lists. As an example, weÂ’ll once more use the bitset class: Comment

//: C05:DotTemplate.cpp

// Illustrate the .template construct

#include <bitset>

#include <cstddef>

#include <iostream>

#include <string>

using namespace std;

 

template<class charT, size_t N>

basic_string<charT> bitsetToString(const bitset<N>& bs) {

  return bs. template to_string<charT, char_traits<charT>,

                                                             allocator<charT> >();

}

 

int main() {

  bitset<10> bs;

  bs.set(1);

  bs.set(5);

  cout << bs << endl; // 0000100010

  string s = bitsetToString<char>(bs);

  cout << s << endl;   // 0000100010

} ///:~

 

The bitset class supports conversion to string object via its to_string member function. To support multiple string classes, to_string is itself a template, following the pattern established by the basic_string template discussed in Chapter 3. The declaration of to_string inside of bitset looks like this: Comment

template <class charT, class traits, class Allocator>

basic_string<charT, traits, Allocator> to_string() const;

 

Our bitsetToString( ) function template above allows you to request different types of string representations of a bitset. To get a wide string, for instance, you change the call to the following:

  wstring s = bitsetToString<wchar_t>(bs);

 

Note that basic_string uses default template arguments, so we donÂ’t have to repeat the char_traits and allocator arguments in the return value. Unfortunately, bitset::to_string does not use default arguments. Using bitsetToString<char>( bs) is more convenient than typing a fully-qualified call to bs.template to_string<char, char_traits, allocator<char> >( ) every time. Comment

The return statement in bitsetToString( ) contains the template keyword in an odd place—right after the dot operator applied to the bitset object bs. This is because when the template is parsed, the < character after the to_string token would be interpreted as a less-than operation instead of the beginning or a template argument list. We explain exactly why this confusion exists in the section “Name lookup issues” later in this chapter. The template keyword used in this context tells the compiler that what follows is the name of a template, causing the < character to be interpreted correctly. The same reasoning applies to the -> and :: operators when applied to templates. As with the typename keyword, this template disambiguation technique can only be used within a template.[50]  Comment

Member Templates

The bitset::to_string( ) function template is an example of a member template: a template declared within another class or class template. This allows many combinations of independent template arguments to be combined. A useful example is found in the complex class template in the standard C+ library. The complex template has a type parameter meant to represent an underlying floating-point type to hold the real and imaginary parts of a complex number. The following code snippet from the standard library shows a member-template constructor in the complex class template: Comment

template<typename T>

class complex {

public:

  template<class X> complex(const complex<X>&);

 

The standard complex template comes ready-made with specializations that use float, double, and long double for the parameter T. The member-template constructor above allows you to create a new complex number that uses a different floating-point type as its base type, as seen in the code below: Comment

  complex<float> z(1,2);

  complex<double> w(z);

 

In the declaration of w, the complex template parameter T is double and X is float. Member templates make this kind of flexible conversion easy. Comment

Since defining a template within a template is a nesting operation, the prefixes that introduce the templates must reflect that nesting if you define the member template outside the outer class definition. For example, if you were to implement the complex class template, and if you were to define the member-template constructor above outside the complex template class definition, you would have to do it like this: Comment

template<typename T>

template<typename X>

complex<T>::complex(const complex<X>& c) {/*body hereÂ…*/}

 

Another use of member function templates in the standard library is in the initialization of containers, such as a vector. Suppose we have a vector of ints and we want to initialize a new vector of doubles with it, like this: Comment

  int data[5] = {1,2,3,4,5};

  vector<int> v1(data, data+5);

  vector<double> v2(v1.begin(), v1.end());

 

As long as the elements in v1 are assignment-compatible with the elements in v2 (as double and int are here), all is well. The vector class template has the following member template constructor: Comment

template <class InputIterator>

vector(InputIterator first, InputIterator last,

           const Allocator& = Allocator());

 

This constructor is actually used twice in the vector declarations above. When v1 is initialized from the array of ints, the type InputIterator is int*. When v2 is initialized from v1, an instance of the member template constructor is used with InputIterator representing vector<int>::iterator. Comment

Member templates can also be classes. (They donÂ’t have to be functions, although thatÂ’s usually what you need.) The following example shows a member class template inside an outer class template. Comment

//: C05:MemberClass.cpp

// A member class template

#include <iostream>

#include <typeinfo>

using namespace std;

 

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

} ///:~

 

The typeid operator, which is covered in Chapter 8, returns an object whose name( ) member function yields a string representation of a type or of the type of a variable. Although the exact representation varies from compiler to compiler, the output of the program above should be something like this: Comment

Outer == int

Inner == bool

Full Inner == Outer<int>::Inner<bool>

 

The declaration of the variable inner in the main program instantiates both Inner<bool> and Outer<int>. Comment

Member template functions cannot be declared virtual. Current compiler technology expects to be able to fix the size of a classÂ’s virtual function table when the class is parsed. Allowing virtual member template functions would require knowing all calls to such member functions everywhere in the program ahead of time, which is not feasible, especially for multi-file projects. Comment

Function template issues

Just as a class template describes a family of classes, a function template describes a family of functions. The syntax for creating either type of template is virtually identical, but they differ somewhat in how they are used. You must always use angle brackets when instantiating class templates and you must supply all non-default template arguments. With function templates, on the other hand, you can often omit the template arguments, and default template arguments are not even allowed. Consider a typical implementation of the min( ) function template declared in the <algorithm> header, which looks something like this: Comment

template<typename T>

const T& min(const T& a, const T& b) {

  return (a < b) ? a : b;

}

 

You could invoke this template by providing the type of the arguments in angle brackets, just like you do with class templates, as in:

int z = min<int>(i, j);

 

This syntax tells the compiler that a specialization of the min template is needed with int used in place of the parameter T, whereupon the compiler generates the corresponding code. Following the pattern of naming the classes generated from class templates, you can think of the name of the instantiated function as min<int>. Comment

Type deduction of function template arguments

You can always use such explicit function template specification as in the example above, but it is often convenient to leave off the template arguments and let the compiler deduce them from the function arguments, like this: Comment

int z = min(i, j);

 

If both i and j are ints, the compiler knows that you need min<int>, which it then instantiates automatically. The types must be identical, because the template was originally specified with only one template type argument used for both function parameters. No standard conversions are applied for function arguments whose type is specified by a template parameter. For example, if you wanted to find the minimum of an int and a double, the following attempt at a call to min would fail: Comment

int z = min(x, j); // x is a double

 

Since x and j are distinct types, no single parameter matches the template parameter T in the definition of min; so the call does not match the template declaration. You can work around this difficulty by casting one argument to the otherÂ’s type or by reverting to the fully-specified call syntax, as in: Comment

int z = min<double>(x, j);

 

This tells the compiler to generate the double version of min, after which j can be promoted to a double by normal standard conversion rules (because the function min<double>(const double&, const double&) would then exist). Comment

You might be tempted to require two parameters for min, allowing the types of the arguments to be independent, like this:

template<typename T, typename U>

const T& min(const T& a, const U& b) {

  return (a < b) ? a : b;

}

 

This is often a good strategy, but in this case it is problematic because min must return a value, and there is no satisfactory way to determine which type it should be (T or U?). Comment

If the return type of a function template is an independent template parameter, you must always specify its type explicitly when you call it, since there is no argument from which to deduce it. Such is the case with the fromString template below. Comment

//: C05:StringConv.h

#ifndef STRINGCONV_H

#define STRINGCONV_H

// Function templates to convert to and from strings

#include <string>

#include <sstream>

 

template<typename T>

T fromString(const std::string& s) {

  std::istringstream is(s);

  T t;

  is >> t;

  return t;

}

 

template<typename T>

std::string toString(const T& t) {

  std::ostringstream s;

  s << t;

  return s.str();

}

#endif // STRINGCONV_H ///:~

 

These function templates provide conversions to and from std:: string for any types that provide a stream inserter or extractor, respectively. HereÂ’s a test program that includes the use of the standard library complex number type: Comment

//: C05:StringConvTest.cpp

#include "StringConv.h"

#include <iostream>

#include <complex>

using namespace std;

 

int main() {

  int i = 1234;

  cout << "i == \"" << toString(i) << "\"\n";

  float x = 567.89;

  cout << "x == \"" << toString(x) << "\"\n";

  complex<float> c(1.0, 2.0);

  cout << "c == \"" << toString(c) << "\"\n";

  cout << endl;

 

  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;

} ///:~

 

The output is what youÂ’d expect: Comment

i == "1234"

x == "567.89"

c == "(1,2)"

 

i == 1234

x == 567.89

c == (1,2)

 

Notice that in each of the instantiations of fromString, the template parameter is specified in the call. If you have a function template with template parameters for the parameter types as well as the return types, it is important to declare the return type parameter first; otherwise you wonÂ’t be able to omit the type parameters for the function parameters. As an illustration, consider the following well-known function template:[51]  Comment

//: C05:ImplicitCast.cpp

template<typename R, typename P>

R implicit_cast(const P& p) {

  return p;

}

 

int main() {

  int i = 1;

  float x = implicit_cast<float>(i);

  int j = implicit_cast<int>(x);

  // char* p = implicit_cast<char*>(i);

} ///:~

 

If you interchange R and P in the template parameter list near the top of the file, it will be impossible to compile this program because the return type will remain unspecified (since the first template parameter would be the functionÂ’s parameter type). The last line (which is commented out) is illegal because there is no standard conversion from int to char*; implicit_cast is for revealing in your code conversions that are allowed naturally. Comment

With a little care you can even deduce array dimensions. The following example has an array-initialization function template (init2) that does just that.

//: C05:ArraySize.cpp

#include <cstddef>

using std::size_t;

 

template<size_t R, size_t C, typename T>

void init1(T a[R][C]) {

  for (size_t i = 0; i < R; ++i)

      for (size_t j = 0; j < C; ++j)

          a[i][j] = T();

}

 

template<size_t R, size_t C, class T>

void init2(T (&a)[R][C]) {   // reference parameter

  for (size_t i = 0; i < R; ++i)

      for (size_t j = 0; j < C; ++j)

          a[i][j] = T();

}

 

int main() {

  int a[10][20];

  init1<10,20>(a);   // must specify

  init2(a);                 // sizes deduced

} ///:~

 

Array dimensions are not passed as part of a function parameterÂ’s type unless that parameter is passed by pointer or reference. The function template init2 declares a to be a reference to a two-dimensional array, so its dimensions R and C are deduced by the template facility, making init2 a handy way to initialize a two-dimensional array of any size. The template init1 does not pass the array by reference, so the sizes must be explicitly specified, although the type parameter can still deduced. Comment

Function template overloading

As with functions, you can overload function templates that have the same name. When the compiler processes a function call in a program, it has to decide which template or ordinary function is the “best” fit for the call. Assuming the existence of the min function template introduced earlier, let’s add some ordinary functions to the mix: Comment

//: C05:MinTest.cpp

#include <cstring>

#include <iostream>

using std::strcmp;

using std::cout;

using std::endl;

 

template<typename 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)

} ///:~

 

In addition to the function template, this program defines two non-template functions: a C-style string version of min and a double version. If the template doesn’t exist at all, the call in line 1 above have invokes the double version of min because of the standard conversion from int to double. Since the template can generate an int version, however, that is considered a better match (of course!); so that’s what happens. The call in line 2 is an exact match for the double version, of course, and the call in line 3 also invokes the same function, implicitly converting 1 to 1.0. In line 4 the const char* version of min is called directly. In line 5 we force the compiler to use the template facility by appending empty angle brackets to the function name, whereupon it generates a const char* version from the template and uses it (which is verified by the wrong answer—it’s just comparing addresses![52]). If you’re wondering why we used using declarations in lieu of the using namespace std; directive, some compilers include headers behind the scenes that bring in std::min, which would conflict with our declarations of the name min. Comment

As stated above, you can overload templates of the same name, as long as they can be distinguished by the compiler. You could, for example, declare a min function template that processes three arguments:

template<typename T>

const T& min(const T& a, const T& b, const T& c);

 

Versions of this template will be generated only for calls to min( ) that have three arguments of the same type. Comment

Taking the address of a generated function template

In a number of situations you need to take the address of a function. For example, you may have a function that takes an argument of a pointer to another function. Of course, itÂ’s possible that this other function might be generated from a template function, so you need some way to take that kind of address:[53]  Comment

//: C05:TemplateFunctionAddress.cpp

// Taking the address of a function generated

// from a template.

 

template <typename T> void f(T*) {}

 

void h(void (*pf)(int*)) {}

 

template <typename T>

  void g(void (*pf)(T*)) {}

 

int main() {

  // Full type specification:

  h(&f<int>);

  // Type deduction:

  h(&f);

  // Full type specification:

  g<int>(&f<int>);

  // Type deduction:

  g(&f<int>);

  // Partial (but sufficient) specification

  g<int>(&f);

} ///:~

 

This example demonstrates a number of issues. First, even though youÂ’re using templates, the signatures must match. The function h( ) takes a pointer to a function that takes an int* and returns void, and thatÂ’s what the template f produces. Second, the function that wants the function pointer as an argument can itself be a template, as in the case of the template g. Comment

In main( ) you can see that type deduction works here too. The first call to h( ) explicitly gives the template argument for f, but since h( ) says that it will only take the address of a function that takes an int*, that part can be deduced by the compiler. With g( ) the situation is even more interesting because two templates are involved. The compiler cannot deduce the type with nothing to go on, but if either f or g is given int, the rest can be deduced. Comment

An obscure issue arises when trying to pass the functions tolower or toupper, declared in <cctype>, as parameters. It is possible to use these in conjunction with the transform algorithm (which is covered in detail in the next chapter), for example, to convert a string to lower or upper case. Care must be taken, however, because there are multiple declarations for these functions. A na ïve approach would is something like this: Comment

// The variable s is a std::string

transform(s.begin(), s.end(), s.begin(), tolower);

 

The transform algorithm applies its fourth parameter (tolower in this case) to each character in the string s and places the result in s itself, thus overwriting each character in s with its lower-case equivalent. As it is written, this statement may or may not work! It fails in the following context: Comment

#include <algorithm>

#include <cctype>

#include <iostream>

#include <string>

using namespace std;

 

int main() {

  string s("LOWER");

  transform(s.begin(),s.end(),s.begin(),tolower);

  cout << s << endl;

}

 

Even if your compiler letÂ’s you get away with this, it is illegal. The reason is that the <iostream> header also makes available a two-argument version of tolower and toupper:

template <class charT> charT toupper(charT c,

                                                                        const locale& loc);

template <class charT> charT tolower(charT c,

                                                                        const locale& loc);

 

These function templates take a second argument of type locale. The compiler has no way of knowing whether it should use the one-argument version of tolower defined in <cctype> or the one mentioned above. You can solve this problem (almost!) with a cast in the call to transform, as follows: Comment

  transform(s.begin(),s.end(),s.begin()

                      static_cast<int (*)(int)>(tolower));

 

(Recall that tolower and toupper traffic in int instead of char.) The cast above makes clear that the single-argument version of tolower is desired. Once again, this works with some compilers, but it is not required to. The reason, albeit obscure, is that a library implementation is allowed to give “C linkage” (meaning that the function name does not contain all the auxiliary information[54]  that normal C++ functions do) to functions inherited from the C language. If this is the case, the cast fails, because transform is a C++ function template and expects its fourth argument to have C++ linkage—and a cast is not allowed to change the linkage. What a predicament! Comment

The solution is to place calls to tolower in an unambiguous context. For example, you could write a function, letÂ’s call it strTolower( ), and place it in its own file without including <iostream>, like this: Comment

//: C05:StrTolower.cpp {O}

#include <algorithm>

#include <cctype>

#include <string>

using namespace std;

 

string strTolower(string s) {

  transform(s.begin(), s.end(), s.begin(), tolower);

  return s;

} ///:~

 

The header <iostream> is not involved here, and the compilers we use do not introduce the two-argument version of tolower in this context,[55]  so thereÂ’s no problem. You can then use this function normally: Comment

//: C05:Tolower.cpp

//{L} StrTolower

#include <algorithm>

#include <cctype>

#include <iostream>

#include <string>

using namespace std;

string strTolower(string);

 

int main() {

  string s("LOWER");

  cout << strTolower(s) << endl;

} ///:~

 

Another solution is to write a wrapper function template that calls the correct version of tolower explicitly:

//: C05:ToLower2.cpp

#include <algorithm>

#include <cctype>

#include <iostream>

#include <string>

using namespace std;

 

template<class charT>

charT strTolower(charT c) {

  return tolower(c);   // one-arg version called

}

 

int main() {

  string s("LOWER");

  transform(s.begin(),s.end(),s.begin(),&strTolower<char>);

  cout << s << endl;

} ///:~

 

This version has the advantage that it can process both wide and narrow strings since the underlying character type is a template parameter. The C++ standards committee is working on modifying the language so that the first example (without the cast) will work, and some day these workarounds can be ignored.[56]  Comment

Applying a function to an STL sequence

Suppose you want to take an STL sequence container (which youÂ’ll learn more about in subsequent chapters; for now we can just use the familiar vector) and apply a function to all the objects it contains. Because a vector can contain any type of object, you need a function that works with any type of vector: Comment

//: C05:ApplySequence.h

// Apply a function to an STL sequence container

 

// 0 arguments, any type of return value:

template<class Seq, class T, class R>

void apply(Seq& sq, R (T::*f)()) {

  typename Seq::iterator it = sq.begin();

  while(it != sq.end()) {

      ((*it)->*f)();

      it++;

  }

}

 

// 1 argument, any type of return value:

template<class Seq, class T, class R, class A>

void apply(Seq& sq, R(T::*f)(A), A a) {

  typename Seq::iterator it = sq.begin();

  while(it != sq.end()) {

      ((*it)->*f)(a);

      it++;

  }

}

 

// 2 arguments, any type of return value:

template<class Seq, class T, class R,

               class A1, class A2>

void apply(Seq& sq, R(T::*f)(A1, A2),

      A1 a1, A2 a2) {

  typename Seq::iterator it = sq.begin();

  while(it != sq.end()) {

      ((*it)->*f)(a1, a2);

      it++;

  }

}

// Etc., to handle maximum likely arguments ///:~

 

The apply( ) function template takes a reference to the container class and a pointer-to-member for a member function of the objects contained in the class. It uses an iterator to move through the Stack and apply the function to every object. Comment

Notice that there are no STL header files (or any header files, for that matter) included in applySequence.h, so it is actually not limited to use with an STL container. However, it does make assumptions (primarily, the name and behavior of the iterator) that apply to STL sequences. Comment

You can see there is more than one version of apply( ), further illustrating overloading of function templates. Although these templates allow any type of return value (which is ignored, but the type information is required to match the pointer-to-member), each version takes a different number of arguments, and because itÂ’s a template, those arguments can be of any type. The only limitation here is that thereÂ’s no “super template” to create templates for you; you must decide how many arguments will ever be required. Comment

To test the various overloaded versions of apply( ), the class Gromit[57]  is created containing functions with different numbers of arguments: Comment

//: C05:Gromit.h

// The techno-dog. Has member functions

// with various numbers of arguments.

#include <iostream>

 

class Gromit {

  int arf;

public:

  Gromit(int arf = 1) : arf(arf + 1) {}

  void speak(int) {

      for(int i = 0; i < arf; i++)

          std::cout << "arf! ";

      std::cout << std::endl;

  }

  char eat(float) {

      std::cout << "chomp!" << std::endl;

      return 'z';

  }

  int sleep(char, double) {

      std::cout << "zzz..." << std::endl;

      return 0;

  }

  void sit() {

      std::cout << " Sitting...)" << std::endl;

  }

}; ///:~

 

Now you can use the apply( ) template functions to apply the Gromit member functions to a vector<Gromit*>, like this: Comment

//: C05:ApplyGromit.cpp

// Test ApplySequence.h

#include <cstddef>

#include <iostream>

#include <vector>

#include "ApplySequence.h"

#include "Gromit.h"

using namespace std;

 

int main() {

  vector<Gromit*> dogs;

  for(size_t i = 0; i < 5; i++)

      dogs.push_back(new Gromit(i));

  apply(dogs, &Gromit::speak, 1);

  apply(dogs, &Gromit::eat, 2.0f);

  apply(dogs, &Gromit::sleep, 'z', 3.0);

  apply(dogs, &Gromit::sit);

  for (size_t i = 0; i < dogs.size(); ++i)

      delete dogs[i];

} ///:~

 

Although the definition of apply( ) is somewhat complex and not something youÂ’d ever expect a novice to understand, its use is remarkably clean and simple, and a novice could easily use it knowing only what it is intended to accomplish, not how. This is the type of division you should strive for in all your program components: The tough details are all isolated on the designerÂ’s side of the wall. Users are concerned only with accomplishing their goals and donÂ’t see, know about, or depend on details of the underlying implementation. WeÂ’ll explore even more flexible ways to apply functions to sequences in the next chapter. Comment

Partial ordering of function templates

We mentioned earlier that an ordinary function overload of min( ) is preferable to using the template. If a function already exists to match a function call, why generate another? In the absence of ordinary functions, however, it is possible that overloaded function templates can lead to ambiguities. To minimize the chances of this, an ordering is defined for function templates that chooses the most specialized template, if such exists. A function template is considered more specialized than another if every possible list of arguments that matches it also matches the other, but not the other way around. Consider the following function template declarations, taken from an example in the C++ standard document: Comment

template<class T> void f(T);

template<class T> void f(T*);

template<class T> void f(const T*);

 

The first template can be matched with any type. The second template is more specialized than the first because only pointer types match it. In other words, you can look upon the set of possible calls that match the second template as a subset of the first. A similar relationship exists between the second and third template declarations above: the third can only be called for pointers to const, but the second accommodates any pointer type. The following program illustrates these rules. Comment

//: C05:PartialOrder.cpp

// Reveals Ordering of Function Templates

#include <iostream>

using namespace std;

 

template<class T>

void f(T) {

  cout << "T\n";

}

 

template<class T>

void f(T*) {

  cout << "T*\n";

}

 

template<class T>

void f(const T*) {

  cout << "const T*\n";

}

 

int main() {

  f(0);                       // T

  int i = 0;

  f(&i);                     // T*

  const int j = 0;

  f(&j);                     // const T*

} ///:~

 

The call f(&i) certainly matches the first template, but since the second is more specialized, it is called. The third canÂ’t be called in this case since the pointer is not a pointer to const. The call f(&j) matches all three templates (for example, T would be const int in the second template), but again, the third template is more specialized, so it is used instead. Comment

If there is no “most specialized” template among a set of overloaded function templates, an ambiguity remains and the compiler will report an error. That is why this feature is called a “partial ordering”—it may not be able to resolve all possibilities. Similar rules exist for class templates (see the section “Partial specialization” below). Comment

Template specialization

The term specialization has a specific, template-related meaning in C++. A template definition is, by its very nature, a generalization, because it describes a family of functions or classes in general terms. When template arguments are supplied, the result is a specialization of the template, because it fixes a unique instance out of the many possible instances of the family of functions or classes. The min function template seen at the beginning of this chapter is a generalization of a minimum-finding function, because the type of its parameters is not specified. When you supply the type for the template parameter, whether explicitly or implicitly via argument deduction, the resultant code generated by the compiler (for example, min<int>) is a specialization of the template. The code generated is also considered an instantiation of the template, of course, as are all code bodies generated by the template facility. Comment

Explicit specialization

You can also provide the code yourself for a given template specialization, should the need arise. Providing your own template specializations is often needed with class templates, but we will begin with the min function template to introduce the syntax. Comment

Recall that in MinTest.cpp earlier in this chapter we introduced the following ordinary function:

const char* min(const char* a, const char* b) {

  return (strcmp(a, b) < 0) ? a : b;

}

 

This was so that a call to min would compare strings and not addresses. Although it would provide no advantage in this case, we could define instead a const char* specialization for min, as in the following program: Comment

//: C05:MinTest2.cpp

#include <cstring>

#include <iostream>

using std::strcmp;

using std::cout;

using std::endl;

 

template<class T> const T& min(const T& a, const T& b) {

  return (a < b) ? a : b;

}

// An explicit specialization of the min template

template<>

const char* const& min<const char*>(const char* const& a,

                                                                      const char* const& b) {

  return (strcmp(a, b) < 0) ? a : b;

}

 

int main() {

  const char *s2 = "say \"Ni-!\"", *s1 = "knights who";

  cout << min(s1, s2) << endl;

  cout << min<>(s1, s2) << endl;

} ///:~

 

The “template<>” prefix tells the compiler that what follows is a specialization of a template. The type for the specialization must appear in angle brackets immediately following the function name, as it normally would in an explicitly-specified call. Note that we carefully substitute const char* for T in the explicit specialization. Whenever the original template specifies const T, that const modifies the whole type T. It is the pointer to a const char* that is const. Therefore we must write const char* const in place of const T in the specialization. When the compiler sees a call to min with const char* arguments in the program, it will instantiate our const char* version of min so it can be called. The two calls to min in this program call the same specialization of min. Comment

Explicit specializations tend to be more useful for class templates than for function templates. When you provide a full specialization for a class template, though, you may need to implement all the member functions. This is because you are providing a separate class, and client code may expect the complete interface to be implemented. Comment

The standard library has an explicit specialization for vector when it is used to hold objects of type bool. As you saw earlier in this chapter, the declaration for the primary vector class template is:

template <class T, class Allocator = allocator<T> >

class vector {Â…};

 

To specialize for objects of type bool, you could declare an explicit specialization as follows:

template <>

class vector< bool, allocator<bool> > {Â…};

 

Again, this is quickly recognized as a full, explicit specialization because of the template<> prefix and because all the primary templateÂ’s parameters are satisfied by the argument list appended to the class name. The purpose for vector<bool> is to allow library implementations to save space by packing bits into integers.[58]  Comment

It turns out that vector<bool> is a little more flexible than we have described, as seen in the next section.

Partial Specialization

Class templates can also be partially specialized, meaning that at least one of the template parameters is left “open” in some way in the specialization. This is actually what vector<bool> does; it specifies the object type (bool), but leaves the allocator type unspecified. Here is the actual declaration of vector<bool>:

template <class Allocator>

class vector<bool, Allocator>;

 

You can recognize a partial specialization because non-empty parameter lists appear in angle brackets both after the template keyword (the unspecified parameters) and after the class (the specified arguments). Because of the way vector<bool> is defined, a user can provide a custom allocator type, even though the contained type of bool is fixed. In other words, specialization, and partial specialization in particular, constitute a sort of “overloading” for class templates. Comment

Partial ordering of class templates

The rules that determine which template is selected for instantiation are similar to the partial ordering for function templates—the “most specialized” template is selected. An illustration follows. (The string in each f( ) member function below explains the role of each template definition.) Comment

//: C05:PartialOrder2.cpp

// Reveals partial ordering of class templates

#include <iostream>

using namespace std;

 

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

} ///:~

 

As you can see, you can partially specify template parameters according to whether they are pointer types, or whether they are equal. When the T* specialization is used, such as is the case in line 5, T itself is not the top-level pointer type that was passed—it is the type that the pointer refers to (float, in this case). The T* specification is a pattern to allow matching against pointer types. If you were to use int** as the first template argument, T would be int*. Line 8 is ambiguous because having the first parameter as an int vs. having the two parameters equal are independent issues—one is not more specialized than the other. Similar logic applies to lines 9 through 12. Comment

A practical example

You can easily derive from a class template, and you can create a new template that instantiates and inherits from an existing template. If the vector template does most everything you want, for example, but in a certain application youÂ’d also like a version that can sort itself, you can easily reuse the vector code. The following example derives from vector<T> and adds sorting. Comment

//: C05:Sorted.h

// Template specialization

#ifndef SORTED_H

#define SORTED_H

#include <string>

#include <vector>

 

template<class T>

class Sorted : public std::vector<T> {

public:

  void sort();

};

 

template<class T>

void Sorted<T>::sort() { // A simple sort

  for(int i = size(); i > 0; i--)

      for(int j = 1; j < i; j++)

          if(at(j-1) > at(j)) {

              T t = at(j-1);

              at(j-1) = at(j);

              at(j) = t;

          }

}

 

// Partial specialization for pointers:

template<class T>

class Sorted<T*> : public std::vector<T*> {

public:

  void sort();

};

 

template<class T>

void Sorted<T*>::sort() {

   for(int i = size(); i > 0; i--)

      for(int j = 1; j < i; j++)

          if(*at(j-1) > *at(j)) {

              T* t = at(j-1);

              at(j-1) = at(j);

              at(j) = t;

          }

}

 

// Full specialization for char*

// (Made inline here for convenience –

//   normally would place function body in separate file

//   and only leave declaration here)

template<>

inline void Sorted<char*>::sort() {

  for(int i = size(); i > 0; i--)

      for(int j = 1; j < i; j++)

          if(std::strcmp(at(j-1), at(j)) > 0) {

              char* t = at(j-1);

              at(j-1) = at(j);

              at(j) = t;

          }

}

#endif // SORTED_H ///:~

 

The Sorted template imposes a restriction on all but one of the classes for which it is instantiated: they must contain a > operator. It works correctly only with non-pointer objects (including objects of built-in types). The full specialization compares the elements using strcmp( ) to sort vectors of char* according to the null-terminated strings to which they refer. Comment

HereÂ’s a driver for Sorted.h that uses the randomizer introduced earlier in the chapter: Comment

//: C05:Sorted.cpp

//{bor} (because of bitset in Urand.h)

// Testing template specialization

#include <cstddef>

#include <iostream>

#include "Sorted.h"

#include "Urand.h"

using namespace std;

 

#define asz(a) (sizeof a / sizeof a[0])

 

char* words[] = {

  "is", "running", "big", "dog", "a",

};

char* words2[] = {

  "this", "that", "theother",

};

 

int main() {

  Sorted<int> is;

  Urand<47> rand;

  for(size_t i = 0; i < 15; i++)

      is.push_back(rand());

  for(size_t i = 0; i < is.size(); i++)

      cout << is[i] << ' ';

  cout << endl;

  is.sort();

  for(size_t i = 0; i < is.size(); i++)

      cout << is[i] << ' ';

  cout << endl;

 

  // Uses the template partial specialization:

  Sorted<string*> ss;

  for(size_t i = 0; i < asz(words); i++)

      ss.push_back(new string(words[i]));

  for(size_t i = 0; i < ss.size(); i++)

      cout << *ss[i] << ' ';

  cout << endl;

  ss.sort();

  for(size_t i = 0; i < ss.size(); i++) {

      cout << *ss[i] << ' ';

      delete ss[i];

  }

  cout << endl;

 

  // Uses the full char* specialization:

  Sorted<char*> scp;

  for(size_t i = 0; i < asz(words2); i++)

      scp.push_back(words2[i]);

  for(size_t i = 0; i < scp.size(); i++)

      cout << scp[i] << ' ';

  cout << endl;

  scp.sort();

  for(size_t i = 0; i < scp.size(); i++)

      cout << scp[i] << ' ';

  cout << endl;

} ///:~

 

Each of the template instantiations above uses a different version of the template. Sorted<int> uses the primary template. Sorted<string*> uses the partial specialization for pointers. Last, Sorted<char*> uses the full specialization for char*. Without this full specialization, you could be fooled into thinking that things were working correctly because the words array would still sort out to “a big dog is running” since the partial specialization would end up comparing the first character of each array. However, words2 would not sort correctly. Comment

Preventing template code bloat

Whenever a class template is instantiated, the code from the class definition for the particular specialization is generated, along with all the member functions that are called in the program. Only the member functions that are actually called are generated. This is a good thing, as the following program makes clear: Comment

//: C05:DelayedInstantiation.cpp

// Member functions of class templates are not

// instantiated until they're needed.

 

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(); // Doesn't create Z<X>::b()

  Z<Y> zy;

  zy.b(); // Doesn't create Z<Y>::a()

} ///:~

 

Here, even though the template Z purports to use both f( ) and g( ) member functions of T, the fact that the program compiles shows you that it only generates Z<X>::a( ) when it is explicitly called for zx. (If Z<X>::b( ) were also generated at the same time, a compile-time error message would be generated, because it would attempt to call X::g( ), which doesnÂ’t exist.) Similarly, the call to zy.b( ) doesnÂ’t generate Z<Y>::a( ). As a result, the Z template can be used with X and Y; whereas if all the member functions were generated when the class was first created the use of many templates would significantly limited. Comment

Suppose you have a container, a Stack say, and you use specializations for int, int*, and char*. Three versions of Stack code will be generated and linked as part of your program. One of the reasons for using a template in the first place is so you don’t have to replicate code by hand; but code still gets replicated—it’s just the compiler that does it instead of you. You can factor the bulk of the implementation for storing pointer types into a single class by using a combination of full and partial specialization. The key is to fully specialize for void* and then derive all other pointer types from the void* implementation so the common code can be shared. The program below illustrates this technique. Comment

//: C05:Nobloat.h

// Shares code for storing pointers in a Stack

#ifndef NOBLOAT_H

#define NOBLOAT_H

#include <cassert>

#include <cstddef>

#include <cstring>

 

// The primary template

template<class T>

class Stack {

  T* data;

  std::size_t count;

  std::size_t capacity;

  enum {INIT = 5};

public:

  Stack() {

      count = 0;

      capacity = INIT;

      data = new T[INIT];

  }

  void push(const T& t) {

      if (count == capacity) {

          // Grow array store

          std::size_t newCapacity = 2*capacity;

          T* newData = new T[newCapacity];

          for (size_t i = 0; i < count; ++i)

              newData[i] = data[i];

          delete [] data;

          data = newData;

          capacity = newCapacity;

      }

      assert(count < capacity);

      data[count++] = t;

  }

  void pop() {

      assert(count > 0);

      --count;

  }

  T top() const {

      assert(count > 0);

      return data[count-1];

  }

  size_t size() const {return count;}

};

 

// Full specialization for void*

template<>

class Stack<void *> {

  void** data;

  std::size_t count;

  std::size_t capacity;

  enum {INIT = 5};

public:

  Stack() {

      count = 0;

      capacity = INIT;

      data = new void*[INIT];

  }

  void push(void* const & t) {

      if (count == capacity) {

         std::size_t newCapacity = 2*capacity;

          void** newData = new void*[newCapacity];

          std::memcpy(newData, data, count*sizeof(void*));

          delete [] data;

          data = newData;

          capacity = newCapacity;

      }

      assert(count < capacity);

     data[count++] = t;

  }

  void pop() {

      assert(count > 0);

      --count;

  }

  void* top() const {

      assert(count > 0);

      return data[count-1];

  }

  std::size_t size() const {return count;}

};

 

// Partial specialization for other pointer types

template<class T>

class Stack<T*> : private Stack<void *> {

  typedef Stack<void *> Base;

public:

  void push(T* const & t) {Base::push(t);}

  void pop() {Base::pop();}

  T* top() const {return static_cast<T*>(Base::top());}

  std::size_t size() {return Base::size();}

};

#endif // NOBLOAT_H ///:~ Comment

 

This simple stack expands as it fills its capacity. The void* specialization stands out as a full specialization by virtue of the template<> prefix (that is, the template parameter list is empty). As mentioned earlier, it is necessary to implement all member functions in a class template specialization. The savings occurs with all other pointer types. The partial specialization for other pointer types derives from Stack<void*> privately, since we are merely using Stack<void*> for implementation purposes, and do not wish to expose any of its interface directly to the user. The member functions for each pointer instantiation are small forwarding functions to the corresponding functions in Stack<void*>. Hence, whenever a pointer type other than void* is instantiated, it is a fraction of the size it would have been had the primary template alone been used.[59]  Here is a driver program: Comment

//: C05:NobloatTest.cpp

#include <iostream>

#include <string>

#include "Nobloat.h"

using namespace std;

 

template<class StackType>

void emptyTheStack(StackType& stk) {

  while (stk.size() > 0) {

      cout << stk.top() << endl;

      stk.pop();

  }

}

// An overload for emptyTheStack (not a specialization!)

template<class T>

void emptyTheStack(Stack<T*>& stk) {

  while (stk.size() > 0) {

      cout << *stk.top() << endl;

      stk.pop();

  }

}

 

int main() {

  Stack<int> s1;

  s1.push(1);

  s1.push(2);

  emptyTheStack(s1);

 

  Stack<int *> s2;

  int i = 3;

  int j = 4;

  s2.push(&i);

  s2.push(&j);

  emptyTheStack(s2);

} ///:~

 

For convenience we have included two emptyStack function templates. Since function templates donÂ’t support partial specialization, we provide overloaded templates. The second version of emptyStack is more specialized than the first, so it is chosen whenever pointer types are used. Three class templates are instantiated in this program: Stack<int>, Stack<void*>, and Stack<int*>. Stack<void*> is implicitly instantiated because Stack<int*> derives from it. If a program uses instantiations for many pointer types, the savings in code size over just using a single Stack template can be substantial. Comment

Name lookup issues

When the compiler encounters an identifier it must determine the type and scope (and in the case of variables, the lifetime) of the entity the identifier represents. This is common knowledge among software developers, but the plot thickens when templates are involved. Because not everything is known about a template when its definition is first seen by the compiler, the compiler must hold off until the template is instantiated before it can determine whether it is being used properly. This predicament leads to a two-phase process for template compilation. Comment

Names in templates

In the first phase the compiler parses the template definition looking for obvious syntax errors and resolving all the names it can. The names it can resolve during parsing are those that do not depend on template parameters, which the compiler takes care of through normal name lookup means (and also through argument-dependent lookup, discussed below, if necessary). The names it canÂ’t resolve are the so-called dependent names, which are names that in some way depend on template parameters. These canÂ’t be resolved until the template is instantiated with its actual arguments. Instantiation, therefore, is the second phase of template compilation. During this second phase, the compiler determines whether an explicit specialization of the template in question needs to be used instead of the primary template. Comment

Before you see an example, two more terms need to be defined. A qualified name is a name with a class-name prefix, a name with an object name and a dot operator, or a name with a pointer to an object and an arrow operator. Examples of qualified names are found in the following expressions: Comment

MyClass::f();

x.f();

p->f();

 

We have used qualified names many times in this book, and most recently in connection with the typename keyword. These are called qualified names because the target names (like f above) are explicitly associated with a class, which tells the compiler where to look for the declarations of those names. Comment

The other term to discuss is argument-dependent lookup[60]  (ADL), which is a technique originally designed to simplify using non-member function calls (including operators) declared in namespaces. Consider the following simple code excerpt: Comment

#include <iostream>

#include <string>

//Â…

  std::string s("hello");

  std::cout << s << std::endl;

 

Note that there is no using namespace std; directive, which is the typical practice inside header files, for example. Without such a directive, it is necessary to use the std:: qualifier on the items that are in the std namespace. We have, however, not qualified everything from std that we are using. Can you see what we have left unqualified? Comment

We have not specified which operator functions to use. We want the following to happen, but we donÂ’t want to have to type it!

std::operator<<(std::operator<<(std::cout,s),std::endl);

 

To make the original output statement work as desired, ADL specifies that when an unqualified function call appears and its declaration is not in (normal) scope, the namespaces (or class scopes) of each of its arguments are searched for a matching function declaration. In the original statement, the first function call is: Comment

operator<<(std::cout, s);

 

Since there is no such function in scope in our original excerpt, the compiler notes that this function’s first argument (std::cout) is in the namespace std; so it adds that namespace to the list of scopes to search for a unique function that best matches the signature operator<<(std::ostream&, std::string). It finds this function declared in the std namespace via the <string> header, so that is the function that is called. Namespaces would be very inconvenient without ADL. (But note that, in general, ADL brings in all declarations of the name in question from all eligible namespaces—if there is no best match, an ambiguity will result.) To turn off ADL, you can enclose the function name in parentheses: Comment

(f)(x, y);   // ADL suppressed

 

Now consider the following program, from a presentation by Herb Sutter:

// Lookup.cpp

// Only works on EDG and Metrowerks (special option)

#include <iostream>

using std::cout;

 

void f(double) { cout << "f(double)\n"; }

 

template<class T>

class X {

public:

  void g() { f(1); }

};

 

void f(int) { cout << "f(int)\n"; }

 

int main() {

  X<int>().g();

}

 

The only compiler we have that gets this correct right out of the box is the Edison Design Group front end. (A number of compilers use this front end, including Comeau C++.) The output should be: Comment

f(double)

 

because f is a non-dependent name that can be resolved early by looking in the context where the template is defined, when only f(double) is in scope. Unfortunately, there is a lot of code in existence that depends on the non-standard behavior of binding the call to f(1) inside g( ) to the latter f(int), so compiler writers have been reluctant to make the change. (Some compilers, such as the Metrowerks compiler, have an option to enable the correct lookup behavior.) Comment

Here is a more detailed example, also based on an example from Herb Sutter:

//: C05:Lookup2.cpp

//{-bor}

//{-g++}

// Microsoft: use option –Za (ANSI mode)

#include <iostream>

#include <typeinfo>

using std::cout;

using std::endl;

 

void g() { cout << "global g()\n"; }

 

template <class T>

class Y {

public:

  void g() { cout << "Y<" << typeid(T).name()

                                  << ">::g()\n"; }

  void h() { cout << "Y<" << typeid(T).name()

                                  << ">::h()\n"; }

  typedef int E;

};

 

typedef double E;

 

template<class T>

void swap(T& t1, T& t2) {

  cout << "global swap\n";

  T temp = t1;

  t1 = t2;

  t2 = temp;

}

 

template<class T>

class X : public Y<T> {

public:

  E f() {

      g();

      this->h();

      T t1 = T(), t2 = T(1);

      cout << t1 << endl;

      swap(t1, t2);

      std::swap(t1, t2);

      cout << typeid(E).name() << endl;

      return E(t2);

  }

};

 

int main() {

  X<int> x;

  cout << x.f() << endl;

} ///:~

 

The output from this program should be:

global g()

Y<int>::h()

0

global swap

double

1

 

Looking at the declarations inside of X::f( ), we observe the following:

·         The return type of X::f( ), which is E, is not a dependent name, so it is looked up when the template is parsed, and the typedef naming E as a double is found. This may seem strange, since with non-template classes the declaration of E in the base class would be found first, but those are the rules. (The base class, Y, is a dependent base class, so it canÂ’t be searched at template definition time). Comment

·         The call to g( ) is also non-dependent, since there is no mention of T. If g had parameters that were of class type of defined in another namespace, ADL would take over, since there is no g with parameters in scope. As it is, this call matches the global declaration of g( ). Comment

·         The call this->h( ) is a qualified name, and the object that qualifies it (this) refers to the current object, which is of type X, which in turn depends on the name Y<T> by inheritance. There is no function h( ) inside of X, so lookup will naturally want to search the scope of XÂ’s base class, Y<T>. Since this is a dependent name, it is looked up at instantiation time, when Y<T> can be reliably known (including any potential specializations that might have been written after the definition of X); so it calls Y<int>::h( ). Comment

·         The declarations of t1 and t2 are dependent, of course. Comment

·         The call to operator<<(cout, t1) is dependent, since t1 is of type T. This is looked up later when T is int, and the inserter for int is found in std. Comment

·         The unqualified call to swap( ) is dependent because its arguments are of type T. This ultimately causes a global swap(int&, int&) to be instantiated, of course. Comment

·         The qualified call to std::swap( ) is not dependent, because std is a fixed namespace; so the compiler knows to look there for the proper declaration. (The qualifier on the left of the “::” must mention a template parameter for a qualified name to be considered dependent.) The std::swap( ) function template later generates std::swap(int&, int&), at instantiation time. No more dependent names remain in X<T>::f( ). Comment

To clarify and summarize: name lookup is done at the point of instantiation if the name is dependent, except that for unqualified dependent names the normal name lookup is also attempted early, at the point of definition. All non-dependent names in templates are looked up early, at the time the template definition is parsed. (If necessary, another lookup occurs at instantiation time, when the type of the actual argument is known.) Comment

(Whew!) If you have studied this example to the point that you understand it, prepare yourself for yet another surprise in the next section when friend declarations enter the picture.

Templates and friends

A friend function declaration inside a class allows a non-member function to access non-public members of that class. If the friend function name is qualified, it will of course be found in the namespace or class that qualifies it. If it is unqualified, however, the compiler must make an assumption about where the definition of the friend function will be, since all identifiers must have a unique scope. The expectation is that the function will be defined in the nearest enclosing namespace (non-class) scope that contains the class granting friendship. Often this is just the global scope. The following non-template example clarifies this issue. Comment

//: C05:FriendScope.cpp

#include <iostream>

using namespace std;

 

class Friendly {

  int i;

public:

  Friendly(int theInt) { i = theInt; }

  friend void f(const Friendly&); // needs global def.

  void g() { f(*this); }

};

 

void h() {

  f(Friendly(1));   // uses ADL

}

 

void f(const Friendly& fo) {   // definition of friend

  cout << fo.i << endl;

}

 

int main() {

  h();                             // prints 1

  Friendly(2).g();     // prints 2

} ///:~

 

The declaration of f( ) inside the Friendly class is unqualified, so the compiler will expect to be able to eventually link that declaration to a definition at file scope (the namespace scope that contains Friendly in this case). That definition appears after the definition of the function h( ). The linking of the call to f( ) inside h( ) to the same function is a separate matter, however. This is resolved by ADL. Since the argument of f( ) inside h( ) is a Friendly object, the Friendly class is searched for a declaration of f( ), which succeeds. If the call were f(1) instead (which makes some sense since 1 can be implicitly converted to Friendly(1)), the call should fail, since there is no hint of where the compiler should look for the declaration of f( ). The EDG compiler correctly complains that f is undefined in that case. Comment

Now suppose that Friendly and f are both templates, as in the following program.

//: C05:FriendScope2.cpp

#include <iostream>

using namespace std;

// Necessary forward declarations

template<class T>

class Friendly;

template<class T>

void f(const Friendly<T>&);

 

template<class T>

class Friendly {

  T t;

public:

  Friendly(const T& theT) : t(theT) {}

  friend void f<>(const Friendly<T>&);

  void g() { f(*this); }

};

 

void h() {

  f(Friendly<int>(1));

}

 

template<class T>

void f(const Friendly<T>& fo) {

  cout << fo.t << endl;

}

 

int main() {

  h();

  Friendly<int>(2).g();

} ///:~

 

First notice that angle brackets in the declaration of f inside Friendly. This is necessary to tell the compiler that f is a template. Otherwise, the compiler will look for an ordinary function named f and of course not find it. We could have inserted the template parameter (<T>) in the brackets, but it is easily deduced from the declaration. Comment

The forward declaration of the function template f before the class definition is necessary, even though it wasnÂ’t in the previous example when f was a not a template; the language specifies that friend function templates must be previously declared. Of course, to properly declare f, Friendly must also have been declared, since f takes a Friendly argument, hence the forward declaration of Friendly in the beginning. We could have placed the full definition of f right after the initial declaration of Friendly instead of separating its definition and declaration, but we chose instead to leave it in a form that more closely resembles the previous example. Comment

One last option remains for using friends inside templates: fully define them inside the class itself. Here is how the previous example would appear with that change:

//: C05:FriendScope3.cpp

//{-bor}

// Microsoft: use the -Za (ANSI-compliant) option

#include <iostream>

using namespace std;

 

template<class T>

class Friendly {

  T t;

public:

  Friendly(const T& theT) : t(theT) {}

  friend void f(const Friendly<T>& fo) {

      cout << fo.t << endl;

}

  void g() { f(*this); }

};

 

void h() {

  f(Friendly<int>(1));

}

 

int main() {

  h();

  Friendly<int>(2).g();

} ///:~

 

There is an important difference between this and the previous example: f is not a template here, but is an ordinary function. (Remember that angle brackets were necessary before to imply that f was a template.) Every time the Friendly class template is instantiated, a new, ordinary function overload is created that takes an argument of the current Friendly specialization. This is what Dan Saks has called “making new friends.”[61]  This is the most convenient way to define friend functions for templates. Comment

To make this perfectly clear, suppose you have a class template to which you want to add non-member operators as friends. Here is a class template that simply holds a generic value:

template<class T>

class Box {

  T t;

public:

  Box(const T& theT) : t(theT) {}

};

 

Without understanding the likes of the previous examples in this section, novices find themselves frustrated because they canÂ’t get a simple stream output inserter to work. If you donÂ’t define your operators inside the definition of Box, you must provide the forward declarations we showed earlier: Comment

//: C05:Box1.cpp

// Defines template operators

#include <iostream>

using namespace std;

// Forward declarations

template<class T>

class Box;

template<class T>

Box<T> operator+(const Box<T>&, const Box<T>&);

template<class T>

ostream& operator<<(ostream&, const Box<T>&);

 

template<class T>

class Box {

  T t;

public:

  Box(const T& theT) : t(theT) {}

  friend Box operator+<>(const Box<T>&, const Box<T>&);

  friend ostream& operator<< <>(ostream&, const Box<T>&);

};

 

template<class T>

Box<T> operator+(const Box<T>& b1, const Box<T>& b2) {

  return Box<T>(b1.t + b2.t);

}

 

template<class T>

ostream& operator<<(ostream& os, const Box<T>& b) {

  return os << '[' << b.t << ']';

}

 

int main() {

  Box<int> b1(1), b2(2);

  cout << b1 + b2 << endl;   // [3]

//   cout << b1 + 2 << endl; // no implicit conversions!

} ///:~

 

Here we are defining both an addition operator and an output stream operator. The main program reveals a disadvantage of this approach: you canÂ’t depend on implicit conversions (see the expression b1 + 2) because templates do not provide them. Using the in-class, non-template approach is shorter and more robust: Comment

//: C05:Box2.cpp

// Defines non-template operators

#include <iostream>

using namespace std;

 

template<class T>

class Box {

  T t;

public:

  Box(const T& theT) : t(theT) {}

  friend Box operator+(const Box<T>& b1,

                                                const Box<T>& b2) {

      return Box<T>(b1.t + b2.t);

  }

  friend ostream& operator<<(ostream& os,

                                                            const Box<T>& b) {

      return os << '[' << b.t << ']';

  }

};

 

int main() {

  Box<int> b1(1), b2(2);

  cout << b1 + b2 << endl;   // [3]

  cout << b1 + 2 << endl;     // [3]

} ///:~

 

Because the operators are normal functions (overloaded for each specialization of Box—just int in this case, of course), implicit conversions are applied as normal; so the expression b1 + 2 is valid. Comment

Friend templates

You can be precise as to which specializations of a template are friends of a class. In the examples in the previous section, only the specialization of the function template f with the same type that specialized Friendly was a friend. For example, only the specialization f<int>(const Friendly<int>&) is a friend of the class Friendly<int>. This was accomplished by using the template parameter for Friendly to specialize f in its friend declaration. If we had wanted to, we could have made a particular, fixed specialization of f a friend to all instances of Friendly, like this: Comment

// Inside Friendly:

  friend void f<>(const Friendly<double>&);

 

By using double instead of T, the double specialization of f has access to the non-public members of any Friendly specialization. The specialization f<double>( ) still isnÂ’t instantiated unless it is explicitly called, of course. Comment

Likewise, if you were to declare a non-template function with no parameters dependent on T, that single function would be a friend to all instances of Friendly: Comment

// Inside of Friendly:

  friend void g(int);   // g(int) befriends all FriendlyÂ’s

 

As always, since g(int) is unqualified, it must be defined at file scope (the namespace scope containing Friendly). Comment

It is also possible to arrange for all specializations of f to be friends for all specializations of Friendly, with a so-called friend template, as follows:

template<class T>

class Friendly {

  template<class U> friend void f<>(const Friendly<U>&);

 

Since the template argument for the friend declaration is independent of T, any combination of T and U is allowed, achieving the friendship objective. Like member templates, friend templates can appear within non-template classes as well. Comment

Template programming idioms

Since language is a tool of thought, new language features tend to spawn new programming techniques. In this section we cover some commonly-used template programming idioms that have emerged in the years since templates were added to the C++ language.[62]  Comment

Traits

The traits template technique, pioneered by Nathan Myers, is a means of bundling type-dependent declarations together. In essence, using traits allows you to “mix and match” certain types and values with contexts that use them in a flexible manner, while keeping your code readable and maintainable. Comment

The simplest example of a traits template is the numeric_limits class template defined in <limits>. The primary template is defined as follows:

template<class T> class numeric_limits {

public:

  static const bool is_specialized = false;

  static T min() throw();

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

  static T round_error() throw();

  static const int min_exponent = 0;

  static const int min_exponent10 = 0;

  static const int max_exponent = 0;

  static const int max_exponent10 = 0;

  static const bool has_infinity = false;

  static const bool has_quiet_NaN = false;

  static const bool has_signaling_NaN = false;

  static const float_denorm_style has_denorm =

                                                                  denorm_absent;

  static const bool has_denorm_loss = false;

  static T infinity() throw();

  static T quiet_NaN() throw();

  static T signaling_NaN() throw();

  static T denorm_min() throw();

  static const bool is_iec559 = false;

  static const bool is_bounded = false;

  static const bool is_modulo = false;

  static const bool traps = false;

  static const bool tinyness_before = false;

  static const float_round_style round_style =

                                                                round_toward_zero;

};

 

The <limits> header defines specializations for all fundamental, numeric types (in which case the member is_specialized is set to true). To obtain the base for the double version of your floating-point number system, for example, you can use the expression numeric_limits<double>::radix. To find the smallest integer value available, you can use numeric_limits<int>::min( ). Not all members of numeric_limits apply to all fundamental types, of course. (For example, epsilon( ) is only for floating-point types.) Comment

The values that will always be integral are static data members of numeric_limits; those that may not be integral, such as the minimum value for float, are implemented as static inline member functions. This is because C++ allows only integral static data member constants to be initialized inside a class definition. Other members, such as floating-point values, must be initialized at file scope outside the class definition, which is not appropriate in a header file. Since the needed value in that case will be placed in an implementation (.cpp) file, the value will not be available for compile-time optimization. Inline member functions of a class template, on the other hand, can be included in a header file, and thus facilitate compile-time optimization. Comment

In Chapter 3 you saw how traits are used to control the character-processing functionality used by the string classes. The classes std::string and std::wstring are specializations of the std::basic_string template, which is defined as follows:

template<class charT,

  class traits = char_traits<charT>,

  class allocator = allocator<charT> >

  class basic_string;

 

The template parameter charT represents the underlying character type, which is usually either char or wchar_t. The primary char_traits template is typically empty, and specializations for char and wchar_t are provided by the standard library. Here is the specification of the specialization char_traits<char> according to the C++ standard: Comment

template<>

struct char_traits<char> {

  typedef char char_type;

  typedef int int_type;

  typedef streamoff off_type;

  typedef streampos pos_type;

  typedef mbstate_t state_type;

  static void assign(char_type& c1, const char_type& c2);

  static bool eq(const char_type& c1, const char_type& c2);

  static bool lt(const char_type& c1, const char_type& c2);

  static int compare(const char_type* s1,

                                        const char_type* s2, size_t n);

  static size_t length(const char_type* s);

  static const char_type* find(const char_type* s,

                                                           size_t n,

                                                            const char_type& a);

  static char_type* move(char_type* s1,

                                                const char_type* s2, size_t n);

  static char_type* copy(char_type* s1,

                                                const char_type* s2, size_t n);

  static char_type* assign(char_type* s, size_t n,

                                                    char_type a);

  static int_type not_eof(const int_type& c);

  static char_type to_char_type(const int_type& c);

  static int_type to_int_type(const char_type& c);

  static bool eq_int_type(const int_type& c1,

                                                  const int_type& c2);

  static int_type eof();

};

 

These functions are used by the basic_string class template for character-based operations common to string processing. When you declare a string variable, such as: Comment

std::string s;

 

you are actually declaring s as follows (because of the default template arguments in the specification of basic_string):

std::basic_string<char, std::char_traits<char>,

                                  std::allocator<char> > s;

 

Because the character traits have been separated from the basic_string class template, you can supply a custom traits class to replace std::char_traits. The following example illustrates this flexibility. Comment

//: C05:PoohCorner.cpp

// Illustrates traits classes

#include <iostream>

using namespace std;

 

// Item classes (traits of guests):

class Water {

public:

  friend ostream& operator<<(ostream& os, const Water&) {

      return os << "Water";

  }

};

class Milk {

public:

  friend ostream& operator<<(ostream& os, const Milk&) {

      return os << "Milk";

  }

};

class Honey {

public:

  friend ostream& operator<<(ostream& os, const Honey&) {

      return os << "Honey";

  }

};

class Cookies {

public:

  friend ostream& operator<<(ostream& os, const Cookies&) {

      return os << "Cookies";

  }

};

 

// Guest classes:

class Bear {

public:

  friend ostream& operator<<(ostream& os, const Bear&) {

      return os << "Pooh";

  }

};

class Boy {

public:

  friend ostream& operator<<(ostream& os, const Boy&) {

      return os << "Christopher Robin";

  }

};

 

// Primary traits template (empty—could hold common types)

template<class Guest>

class GuestTraits;

 

// Traits specializations for Guest types

template<>

class GuestTraits<Bear> {

public:

  typedef Water beverage_type;

  typedef Honey snack_type;

};

template<>

class GuestTraits<Boy> {

public:

  typedef Milk beverage_type;

  typedef Cookies snack_type;

};

 

// A custom traits class

class MixedUpTraits {

public:

  typedef Milk beverage_type;

  typedef Honey snack_type;

};

 

// The Guest template (uses a traits class)

template< class Guest, class traits = GuestTraits<Guest> >

class PoohCorner {

  Guest theGuest;

  typedef typename traits::beverage_type beverage_type;

  typedef typename traits::snack_type snack_type;

  beverage_type bev;

  snack_type snack;

public:

  PoohCorner(const Guest& g)

      : theGuest(g), bev(beverage_type()),

         snack(snack_type()) {}

  void entertain() {

      cout << "Entertaining " << theGuest

                << " serving " << bev

                << " and " << snack << endl;

  }

};

 

int main() {

  Boy cr;

  PoohCorner<Boy> pc1(cr);

  pc1.entertain();

  Bear pb;

  PoohCorner<Bear> pc2(pb);

  pc2.entertain();

  PoohCorner<Bear, MixedUpTraits> pc3(pb);

  pc3.entertain();

} ///:~ Comment

 

In this program, instances of the guest classes Boy and Bear are served items appropriate to their tastes. Boys like milk and cookies and Bears like water and honey. This association of guests to items is done via specializations of a primary (empty) traits class template. The default arguments to PoohCorner ensure that guests get their proper items, but you can override this by simply providing a class that meets the requirements of the traits class, as we do with the MixedUpTraits class above. The output of this program is: Comment

Entertaining Christopher Robin serving Milk and Cookies

Entertaining Pooh serving Water and Honey

Entertaining Pooh serving Milk and Honey

 

Using traits provides two key advantages: (1) it allows flexibility in pairing objects with associated attributes or functionality, and (2) it keeps template parameter lists small and readable. If 30 types were associated with a guest, it would be inconvenient to have to specify all 30 arguments directly in each PoohCorner declaration. Factoring the types into a separate traits class simplifies things considerably. Comment

The traits technique is also used in implementing streams and locales, as we showed in Chapter 4.

Policies

If you inspect the char_traits specialization for wchar_t, youÂ’ll see that it is practically identical to its char counterpart:

template<>

  struct char_traits<wchar_t> {

  typedef wchar_t char_type;

  typedef wint_t int_type;

  typedef streamoff off_type;

  typedef wstreampos pos_type;

  typedef mbstate_t state_type;

  static void assign(char_type& c1, const char_type& c2);

  static bool eq(const char_type& c1, const char_type& c2);

  static bool lt(const char_type& c1, const char_type& c2);

  static int compare(const char_type* s1,

                                        const char_type*   s2, size_t n);

  static size_t length(const char_type* s);

  static const char_type* find(const char_type* s,

                                                            size_t n,

                                                            const char_type& a);

  static char_type* move(char_type* s1,

                                                const char_type* s2, size_t n);

  static char_type* copy(char_type* s1,

                                                const char_type* s2, size_t n);

  static char_type* assign(char_type* s, size_t n,

                                                    char_type a);

  static int_type not_eof(const int_type& c);

  static char_type to_char_type(const int_type& c);

  static int_type to_int_type(const char_type& c);

  static bool eq_int_type(const int_type& c1,

                                                  const int_type& c2);

  static int_type eof();

};

 

The only real difference between the two versions is the set of types involved (char and int vs. wchar_t and wint_t). The functionality provided is the same.[63]  This highlights the fact that traits classes are indeed for traits, and therefore the things that change between related traits classes are usually types and constant values, or fixed algorithms that use type-related template parameters. Traits classes tend to be templates themselves, since the types and constants they contain are seen as characteristics of the primary template parameter(s) (for example, char and wchar_t). Comment

It is also useful to be able to associate functionality with template arguments, so that client programmers can easily customize behavior when they code. The following version of the PoohCorner program, for instance, supports different types of entertainment: Comment

//: C05:PoohCorner2.cpp

// Illustrates policy classes

#include <iostream>

using namespace std;

 

// Item classes:

class Water {

public:

  friend ostream& operator<<(ostream& os, const Water&) {

      return os << "Water";

  }

};

class Milk {

public:

  friend ostream& operator<<(ostream& os, const Milk&) {

      return os << "Milk";

  }

};

class Honey {

public:

  friend ostream& operator<<(ostream& os, const Honey&) {

      return os << "Honey";

  }

};

class Cookies {

public:

  friend ostream& operator<<(ostream& os, const Cookies&) {

      return os << "Cookies";

  }

};

 

// Guest classes:

class Bear {

public:

  friend ostream& operator<<(ostream& os, const Bear&) {

      return os << "Pooh";

  }

};

class Boy {

public:

  friend ostream& operator<<(ostream& os, const Boy&) {

      return os << "Christopher Robin";

  }

};

 

// Traits template

template<class Guest>

class GuestTraits;

 

// Traits specializations for Guest types

template<>

class GuestTraits<Bear> {

public:

  typedef Water beverage_type;

  typedef Honey snack_type;

};

template<>

class GuestTraits<Boy> {

public:

  typedef Milk beverage_type;

  typedef Cookies snack_type;

};

 

// Policy classes (require a static doAction() function)

class Feed {

public:

  static const char* doAction() {

      return "Feeding";

  }

};

class Stuff {

public:

  static const char* doAction() {

      return "Stuffing";

  }

};

 

// The Guest template (uses a policy and a traits class)

template< class Guest, class Action, class traits =

                                                                       GuestTraits<Guest> >

class PoohCorner {

  Guest theGuest;

  typedef typename traits::beverage_type beverage_type;

  typedef typename traits::snack_type snack_type;