Navigation



14.4 — Expression parameters and template specialization

In previous lessons, you’ve learned how to use template type parameters to create functions and classes that are type independent. However, template type parameters are not the only type of template parameters available. Template classes (not template functions) can make use of another kind of template parameter known as an expression parameter.

Expression parameters

A template expression parameter is a parameter that does not substitute for a type, but is instead replaced by a value. An expression parameter can be any of the following:

  • A value that has an integral type or enumeration
  • A pointer or reference to an object
  • A pointer or reference to a function
  • A pointer or reference to a class member function

In the following example, we create a buffer class that uses both a type parameter and an expression parameter. The type parameter controls the data type of the buffer array, and the expression parameter controls how large the buffer array is.

template <typename T, int nSize> // nSize is the expression parameter
class Buffer
{
private:
    // The expression parameter controls the size of the array
    T m_atBuffer[nSize];

public:
    T* GetBuffer() { return m_atBuffer; }

    T& operator[](int nIndex)
    {
        return m_atBuffer[nIndex];
    }
};

int main()
{
    // declare an integer buffer with room for 12 chars
    Buffer<int, 12> cIntBuffer;

    // Fill it up in order, then print it backwards
    for (int nCount=0; nCount < 12; nCount++)
        cIntBuffer[nCount] = nCount;

    for (int nCount=11; nCount >= 0; nCount--)
        std::cout << cIntBuffer[nCount] << " ";
    std::cout << std::endl;

    // declare a char buffer with room for 31 chars
    Buffer<char, 31> cCharBuffer;

    // strcpy a string into the buffer and print it
    strcpy(cCharBuffer.GetBuffer(), "Hello there!");
    std::cout << cCharBuffer.GetBuffer() << std::endl;

    return 0;
}

This code produces the following:

11 10 9 8 7 6 5 4 3 2 1 0
Hello there!

One noteworthy thing about the above example is that we do not have to dynamically allocate the m_atBuffer member array! This is because for any given instance of the Buffer class, nSize is actually constant. For example, if you instantiate a Buffer, the compiler replaces nSize with 12. Thus m_atBuffer is of type int[12], which can be allocated statically.

Template specialization

When instantiating a template class for a given type, the compiler stencils out a copy of each templated member function, and replaces the template type parameters with the actual types used in the variable declaration. This means a particular member function will have the same implementation details for each instanced type. While most of the time, this is exactly what you want, occasionally there are cases where it is useful to implement a templated member function slightly different for a specific data type. Template specialization lets you accomplish exactly this.

Let’s take a look at a very simple example:

using namespace std;

template <typename T>
class Storage
{
private:
    T m_tValue;
public:
    Storage(T tValue)
    {
         m_tValue = tValue;
    }

    ~Storage()
    {
    }

    void Print()
    {
        std::cout << m_tValue << std::endl;;
    }
};

The above code will work fine for many data types:

int main()
{
    // Define some storage units
    Storage<int> nValue(5);
    Storage<double> dValue(6.7);

    // Print out some values
    nValue.Print();
    dValue.Print();
}

This prints:

5
6.7

Now, let’s say we want double values to output in scientific notation. To do so, we will need to use template specialization to create a specialized version of the Print() function for doubles. This is extremely simple: simply define the specialized function outside of the class definition, replacing the template type with the specific type you wish to redefine the function for. Here is our specialized Print() function for doubles:

void Storage<double>::Print()
{
    std::cout << std::scientific << m_tValue << std::endl;
}

When the compiler goes to instantiate Storage<double>::Print(), it will see we’ve already defined one, and it will use the one we’ve defined instead of stenciling out a version from the generic templated member function.

As a result, when we rerun the above program, it will print:

5
6.700000e+000

Now let’s take a look at another example where template specialization can be useful. Consider what happens if we try to use our templated Storage class with datatype char*:

int main()
{
    using namespace std;

    // Dynamically allocate a temporary string
    char *strString = new char[40];

    // Ask user for their name
    cout << "Enter your name: ";
    cin >> strString;

    // Store the name
    Storage<char*> strValue(strString);

    // Delete the temporary string
    delete strString;

    // Print out our value
    strValue.Print(); // This will print garbage
}

As it turns out, instead of printing the name the user input, strValue.Print() prints garbage! What’s going on here?

When Storage is instantiated for type char*, the constructor for Storage<char*> looks like this:

Storage<char*>::Storage(char* tValue)
{
     m_tValue = tValue;
}

In other words, this just does a pointer assignment! As a result, m_tValue ends up pointing at the same memory location as strString. When we delete strString in main(), we end up deleting the value that m_tValue was pointing at! And thus, we get garbage when trying to print that value.

Fortunately, we can fix this problem using template specialization. Instead of doing a pointer copy, we’d really like our constructor to make a copy of the input string. So let’s write a specialized constructor for datatype char* that does exactly that:

Storage<char*>::Storage(char* tValue)
{
    // Allocate memory to hold the tValue string
    m_tValue = new char[strlen(tValue)+1];
    // Copy the actual tValue string into the m_tValue memory we just allocated
    strcpy(m_tValue, tValue);
}

Now when we allocate a variable of type Storage<char*>, this constructor will get used instead of the default one. As a result, m_tValue will receive its own copy of strString. Consequently, when we delete strString, m_tValue will be unaffected.

However, this class now has a memory leak for type char*, because m_tValue will not be deleted when the Storage variable goes out of scope. As you might have guessed, this can also be solved by specializing the Storage<char*> destructor:

Storage<char*>::~Storage()
{
    delete[] m_tValue;
}

Now when variables of type ~Storage<char*> go out of scope, the memory allocated in the specialized constructor will be deleted in the specialized destructor.

14.5 — Class template specialization
Index
14.3 — Template Classes

23 comments to 14.4 — Expression parameters and template specialization

  • [...] 14.4 — Expression parameters and template specialization [...]

  • Sean

    Alex,
    I just finished your awesome tutorial, I felt my understanding to C++ really improved a lot.
    Would you please recommand a couple of C++ books because I really want to keep all the learnings up and may dig more into it?
    I am a VBA programmer for a while and want to be involved in C++ programming work. I think the only way to improve C++ programming skill is to do the real world programming. I am not a CS major. Is there any tests that can tell employers my C++ level and help me land with some entry level C++ programmer?

    Thanks a lot,

    Sean,

    • I’m not really sure what to recommend to you bookwise, as you’ll find a lot of books are rather redundant with this tutorial. Instead, I would highly advise assigning yourself a project that will test your skills and make you put the concepts you’ve learned into play. You’ll learn more from doing that than anything at this point.

      I do not know of any standardized C++ proficiency tests. Many employers have their own versions and will have you take them when you apply (if they like your resume enough).

  • Ben

    To specialize a template there still has to be the template keyword before the specialized function, followed by an empty parameter list; i.e. this won’t compile:

    1. void Storage<double>::Print()
    2. {
    3. std::cout << scientific << m_tValue << std::endl;
    4. }

    This is how it has to be:

    1. template
    2. void Storage::Print()
    3. {
    4. std::cout << scientific << m_tValue << std::endl;
    5. }

    • Ben

      No, sorry, this

      1. template
      2. void Storage::Print()
      3. {
      4. std::cout << scientific << m_tValue << std::endl;
      5. }

      is not how it has to be – the html paser killed the empty parameter list, this should be better:

      1. template <>
      2. void Storage::Print()
      3. {
      4. std::cout << scientific << m_tValue << std::endl;
      5. }

      • That doesn’t make any sense to me. If you have an empty parameter list, how would the compiler know which template type to specialize the function for?

        • Hi Ben,Alex

          VS2005 does accept template <> but it seems to be optional. I think it is just a way to tell the compiler that you are intending to specialize a template function – so using template <> before any non-template overriding function generates an error.

          • Cuacharpanas

            May be it is a compiler issue because if i don’t use template followed by the angled brackets before the line Storage::Print() then the compiler (Borland C++ 5.5.1 for Win32) tells: Explicit especialization of ‘void Storage::Print()’ requires ‘template’ declaration. Its stressing that some syntax have to depend on the compiler used.

            • DOBRESCU Mihai

              Same thing happens in Code::Blocks. Without the “template <>” line before the specialized function, the GNU GCC Compiler says:

              “main.cpp|43|error: specializing member ‘Storage::~ Storage’ requires ‘template <>’ syntax”

  • [...] 14.4 — Expression parameters and template specialization [...]

  • [...] 14.4 — Expression parameters and template specialization [...]

  • davidv
    void Storage<double>::Print()
    {
        std::cout << scientific << m_tValue << std::endl;
    }
    

    I think this should be std::scientific in order to work.

    [ I believe you are correct. Thanks! -Alex ]

  • gswrg

    Hi Alex,

    great tutorial. Thanks a lot.

    First example in line 11 should read

     T& operator[](int nIndex)   

    instead of

     int& operator[](int nIndex)   

    [ Yes, thank you. -Alex ]

  • M.N. Jayaram

    Hi

    A small typo. In the class Buffer the first comment has ‘side’ of array instead of ‘size’.

    Not nitpicking, just pointing!

    [ Thanks for letting me know! -Alex ]

  • [...] and elements.  A more readable description of how to do template specialisation can be found here.  The quick summary is [...]

  • MD AZAD ALI SHAH

    Hi Alex,

    Thanks a lot for the awsome tutorial. Its really wonderful.
    Just a small doubt. You say above-

    “Template classes (not template functions) can make use of another kind of template parameter known as an expression parameter.”

    I think expression parameter can be used for Temple functions as well.
    You also did same in 14.6 chapter in one of the example:-

    1.template<int nSize>
    2.void PrintBufferString(Buffer<char, nSize> &rcBuf)
    3.{
    4.
    std::cout << rcBuf.GetBuffer() << std::endl;
    5.}
    

    Warm Rgds,
    Muhammad Azad Ali Shah

  • MD AZAD ALI SHAH

    Now, let’s say we want double values to output in scientific notation. To do so, we will need to use template specialization to create a specialized version of the Print() function for doubles. This is extremely simple: simply define the specialized function outside of the class definition, replacing the template type with the specific type you wish to redefine the function for. Here is our specialized Print() function for doubles:

    1.void Storage<double>::Print()
    2.{
    3.
    std::cout << std::scientific << m_tValue << std::endl;
    4.}
    

    ———————————–
    Hi Alex,

    Can it be :

      template<>
    1.void Storage<double>::Print()
    2.{
    3.
    std::cout << std::scientific << m_tValue << std::endl;
    4.}
    
  • webbnh

    In your concluding sentence, I don’t think you want a tilde before the type name.

    (Nevertheless, I, too, think this tutorial is great, and I refer to it constantly. Thanks!)

  • SUN

    Hi Alex,

    Thanks a lot for the tutorial.

    The code below
    26 for (int nCount=11; nCount >= 0; nCount–)

    should be

    26 for (nCount=11; nCount >= 0; nCount–)

    To avoid error ncount’ : redefinition; multiple initialization during compilation.

    Thanks
    SUN

  • [...] 14.4 Expression parameters and template specialization [...]

  • jp

    for 5th code block

        // Store the name
        Storage<char*> strValue(strString);
    
        // Delete the temporary string
        delete strString;
    

    shouldn’t it be delete[] since strString is dynamically allocated c style string..

  • [...] the previous lesson on template specialization, we saw how it was possible to specialize member functions of a template class in order to provide [...]

  • [...] the lesson on expression parameters and template specialization, you learned how expression parameters could be used to parametrize template [...]

You must be logged in to post a comment.