Home arrow C++ arrow Page 2 - An Introduction to C++ Templates

An Introduction to C++ Templates

Do you feel as though a subset of functions within a class are preforming the same tasks, and only differ by datatype? The power of C++ templates is that they allow alogorithms to be abstracted from their data types. Read more ...

Author Info:
By: Kais Dukes
Rating: 4 stars4 stars4 stars4 stars4 stars / 51
May 19, 2003
  1. · An Introduction to C++ Templates
  2. · C Templates
  3. · Conclusion

print this article

An Introduction to C++ Templates - C Templates
(Page 2 of 3 )

Function Templates

By using function overloading, we can perform the same operation on a variety of different data types. To perform addition on both a pair of integers as well as a pair of doubles, we could use an overloaded function:

int add(const int x, const int y)
   return x + y;
double add(const double x, const double y)
   return x + y;

In this case, the compiler will correctly resolve a function call based on the types of its parameters.

// Calls int add(const int, const int);
const int z1 = add(3, 2);
// Calls double add(const double, const double);
const double z2 = add(3.0, 2.0);

If we need to deal with another type, we would have to provide another function overload. While each of the function overloads uses a different data type, they each follow the same pattern , and so in principle the compiler could generate this code for us, whenever we need to call the add function on a particular data type. A template function does exactly this:

template<class T>
const T add(const T &t1, const T &t2)
   return t1 + t2;

Conceptually, the compiler recognizes the template through the template keyword, which is followed by the template parameter list , consisting of one or more template parameters . When add is called on a particular type, the compiler will take the template definition and replace any occurrences of the template parameters with the given types.

In this example, the template paramter list consists of the single type template parameter T. Using a template function differs from function overloading because the compiler automatically generates code for each new type that is required.

We can use the add template with any type that has an operator + defined. Suppose a custom String class provides string concatenation and knows how to write itself to std::ostream. Because String is compatible with the template function, we can call it to perform string addition:

// Sample strings.
const String strBook("book");
const String strWorm("worm");
// Displays "bookworm".
cout << add(strBook, strWorm) << endl;

Seeing that we intended to add two String values, the compiler will generate the appropriate add function on our behalf, which would look something like:

const String add(const String &t1, const String &t2)
   return t1 + t2;

Explicit Instantiation

When calling the template function, the compiler will choose the correct type used to instantiate the template. Although the standard allows for explicit template instantiation, not all vendors have correctly implemented it. For example, the 6.0 release of Visual C++ silently calls the wrong function:

template<class T>
int getSize(void)
   return sizeof(T);
// Outputs 4, should be 8.
cout << "double: " << getSize<double>() << endl;
// Outputs 4, correctly.
cout << "int: " << getSize<int>() << endl;

Designers of cross-platform code may not want to rely on explicit template function instantiation until better compiler support is available.

Class Templates

The analogy of function templates also applies to classes. Templates can be used to provide a family of classes from a generic pattern. If we needed a full complement of arithmetic to supplement the add function, we could consider using a class. If the actual operands changed infrequently relative to the number of function calls, it would make sense to make the two arguments part of the class itself. With templates, it is possible to create a generic class that is parameterized on another type:

template<class T>
class CCalculator
      CCalculator(const T &x, const T &y) : m_x(x), m_y(y){ }
      ~CCalculator(void){ }
      const T add(void){ return m_x + m_y; }
      const T sub(void){ return m_x - m_y; }
      const T mult(void){ return m_x * m_y; }
      const T div(void){ return m_x / m_y; }
      const T m_x;
      const T m_y;

To instantiate this template class, we need to supply a specific type:

// Create a calculator for integers.
CCalculator<int> calc(5, 2);
// Should give 10.
const int z = calc.mult();

As for function templates, the compiler will create a class for each type that it supplied to the template. This provides a powerful mechanism for code reuse, allowing a single template to act on any compatible data type.

Template Compilation Models

When writing template classes, function definitions are usually stored in a header file along with their declarations, and not in a separate .cpp file. Trying to do otherwise typically will result in a linker error. This is because most compilers require template definitions to be available to each translation unit that uses them, through the inclusion of header files.

The reason for this behaviour is that templates are only a pattern, and as such they do not directly produce code until the compiler comes a across an instantiation. If we create a CCalculator instance, and invoke one of its class methods, the compiler will need to have the function definition at hand.

If the header file is included and contains this definition, all is well. But if the definition resides in a .cpp file, the compiler cannot be expected to find the pattern at the time, and use it to generate the required code. However, the C++ standard does provide a mechanism to aid compilers. The export keyword instructs the compiler that we are providing a separately compiled template:

// In MyTemplateFunction.h
template<class T>
void myTemplateFunction(const T &t1);
// In MyTemplateFunction.cpp
export template <class T>
void myTemplateFunction(const T &t1)

Today, most compilers require template definitions to be explicitly added to a translation unit through header file inclusion, although the standard does cater to definitions that reside in .cpp files. The two different template compilation models are traditionally known as the inclusion model and the separation model .

At the time of writing, the only compiler that I know of which supports the separation model is the Comeau C++ compiler. The Comeau implementation goes some way to support the usage of the export keyword as intended by the standard, but is currently still in beta release.

The typename Keyword

Another keyword related to the use of templates is the typename keyword, which has two uses. Consider the following template class.

template<class T>
void myFunction(void)
   // This is problematic.
   T::x1 * x2;

An initial reading might indicate that myFunction declares a pointer x2 of type T::x1. However, the function could also be invoking the binary multiplication operator on a member variable x1 of the class T and a global variable x2. Using the typename keyword instructs the compiler that an unknown identifier is a type:

// T:x1 is a type and x2 is a pointer
typename T:x1* x2;

The second use of the typename keyword is in place of the class keyword when specifying template parameters:
// These two are identical ...
template<class T1, class T2>;
template<typename T1, typename T2>;

The standard allows for the use of either, and the choice is purely stylistic.

Member Function Templates

In addition to global template functions, the language also supports member template functions: A class may have a member function that uses a template parameter list. Consider the following non-template class, in which the constructor has been templated:

class CTypeSize
      template<class T>
      CTypeSize(const T &t1) :
      ~CTypeSize(void){ };
      int getSize(void) const{ return m_nSize; }
      const int m_nSize;

When a template member function is called, the compiler will use the template pattern to generate code for the given type. In this case, we can create an instance of CTypeSize by providing an variable of any type:

// Displays 12.
CTypeSize t1("Hello World");
cout << t1.getSize() << endl;
// Displays 8 on VC++ 6 / win32.
CTypeSize t2(7.0);
cout << t2.getSize() << endl;

Sometimes member templates are the most effective way to implement a copy constructor. Consider a simple container that holds a single value:
template<class T>
class CSingle
      CSingle(const T &t1) : m_Value(t1) { }
      ~CSingle(void){ }
      T m_Value;

This then leads to the following problem:
// Create an integer container.
CSingle<int> x(7);
// This needs a copy constructor ...
CSingle<double> y(x);

Using member templates, the copy constructor is simple:

template<class S>
CSingle(const CSingle<S> &s1) : m_Value(s1.m_Value) { }

When the compiler can convert an instance of type T into an instance of type S, this will work; this is the case because a double can be constructed from an integer.

blog comments powered by Disqus

- Intel Threading Building Blocks
- Threading Building Blocks with C++
- Video Memory Programming in Text Mode
- More Tricks to Gain Speed in Programming Con...
- Easy and Efficient Programming for Contests
- Preparing For Programming Contests
- Programming Contests: Why Bother?
- Polymorphism in C++
- Overview of Virtual Functions
- Inheritance in C++
- Extending the Basic Streams in C++
- Using Stringstreams in C++
- Custom Stream Manipulation in C++
- General Stream Manipulation in C++
- Serialize Your Class into Streams in C++

Watch our Tech Videos 
Dev Articles Forums 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us 
Weekly Newsletter
Developer Updates  
Free Website Content 
Contact Us 
Site Map 
Privacy Policy 

Developer Shed Affiliates


© 2003-2019 by Developer Shed. All rights reserved. DS Cluster - Follow our Sitemap
Popular Web Development Topics
All Web Development Tutorials