C++
  Home arrow C++ arrow Page 2 - An Introduction to C++ Templates
Dev Articles Forums 
ADO.NET  
Apache  
ASP  
ASP.NET  
C#  
C++  
ColdFusion  
COM/COM+  
Delphi-Kylix  
Design Usability  
Development Cycles  
DHTML  
Embedded Tools  
Flash  
Graphic Design  
HTML  
IIS  
Interviews  
Java  
JavaScript  
MySQL  
Oracle  
Photoshop  
PHP  
Reviews  
Ruby-on-Rails  
SQL  
SQL Server  
Style Sheets  
VB.Net  
Visual Basic  
Web Authoring  
Web Services  
Web Standards  
XML  
Mobile Linux 
App Generation ROI 
IBM® developerWorks 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us Get Paid 
Request Media Kit
Contact Us 
Site Map 
Privacy Policy 
Support 
 USERNAME
 
 PASSWORD
 
 
  >>> SIGN UP!  
  Lost Password? 
C++

An Introduction to C++ Templates
By: Kais Dukes
  • Search For More Articles!
  • Disclaimer
  • Author Terms
  • Rating: 4 stars4 stars4 stars4 stars4 stars / 34
    2003-05-19

    Table of Contents:
  • An Introduction to C++ Templates
  • C Templates
  • Conclusion

  • Rate this Article: Poor Best 
      ADD THIS ARTICLE TO:
      Del.ici.ous Digg
      Blink Simpy
      Google Spurl
      Y! MyWeb Furl
    Email Me Similar Content When Posted
    Add Developer Shed Article Feed To Your Site
    Email Article To Friend
    Print Version Of Article
    PDF Version Of Article
     
     
    ADVERTISEMENT


    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
    {
       public:
          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; }
       private:
          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
    {
       public:
          template<class T>
          CTypeSize(const T &t1) :
          m_nSize(sizeof(t1))
          {
          }
          ~CTypeSize(void){ };
          int getSize(void) const{ return m_nSize; }
       private:
          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
    {
       public:
          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.

    More C++ Articles
    More By Kais Dukes


       · Thanx dev, This topic give me basic knowledge.Now i can start deep study on...
     

    C++ ARTICLES

    - 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++
    - Advanced File Handling with Streams in C++
    - File Handling and Streams in C++
    - The STL String Class
    - Iostream Library and Basic I/O in C++
    - Introduction to Streams







    © 2003-2009 by Developer Shed. All rights reserved. DS Cluster 1 hosted by Hostway
    Stay green...Green IT