Search

14.6 — Partial template specialization

In the lesson on expression parameters and template specialization, you learned how expression parameters could be used to parametrize template classes.

Let’s take another look at the Buffer class we used in the previous example:

Now, let’s say we wanted to write a function to print out a buffer as a string. Although we could implement this as a member function, we’re going to do it as a non-member function instead because it will make the successive examples easier to follow.

Using templates, we might write something like this:

This would allow us to do the following:

and get the following result:

Ten

Although this works, it has a design flaw. Consider the following:

This program will compile, execute, and produce the following value (or one similar):

0012FF10

What happened? PrintBufferString() has std::cout print the value of rcBuf.GetBuffer(), which returns a pointer to m_atBuffer! When the data type is a char, cout will print the array as a C-style character string, but when the data type is non-char (such as in this case), cout will print the address that the pointer is holding!

Obviously this case exposes a misuse of this function (as written). Without explicitly examining the code, the programmer would not have any clue that this function does not handle non-char buffers correctly. This is likely to lead to programming errors.

Template specialization

One seemingly useful way to solve this problem is to use template specialization to ensure that only arrays of type char can be passed to PrintBufferString(). As you learned in the previous lesson, template specialization allows you to define a function where all of the templated types have been resolved to a specific data type.

Here’s an example of how that might work here:

As you can see, we’ve now specialized PrintBufferString so it will only accept Buffers of type char and of length 10. This means if we try to call PrintBufferString with an int buffer, the compiler will give us an error.

Although this solves the issue of making sure PrintBufferString can not be called with an int Buffer, it brings up another problem: using full template specialization means we have to explicitly define the length of the buffer this function will accept! Consider the following example:

Trying to call PrintBufferString() with cChar11Buffer will not work, because cChar11Buffer is a class of type Buffer<char, 11>, and PrintBufferString() only accepts classes of type Buffer<char, 10>. Even though Buffer<char, 10> and Buffer<char, 11> are both templated from the generic Buffer class, the different template parameters means they are treated as different classes, and can not be intermixed.

Although we could make a copy of PrintBufferString() that could handle Buffer<char, 11>, what happens when we want to call PrintBufferString() will a buffer of size 5, or 14? We’d have to copy the function for each different Buffer size we wanted to use.

Obviously full template specialization is too restrictive a solution here. The solution we are looking for is partial template specialization.

Partial template specialization

Partial template specialization allows us to write functions where some of the template parameters have been fully or partially resolved. In this case, the ideal solution would be to allow PrintBufferString() to accept char Buffers of any length. That means we have to specialize the templated data type, but leave the length in templated form. Fortunately, partial template specialization allows us to do just that!

As you can see here, we’ve explicitly declared that this function will only work for Buffers of type char, but nSize is still a templated parameter, so it will work for char buffers of any size. That’s all there is to it!

Consider the following example:

This prints:

Ten
Eleven

Just as we expect.

Partial template specialization for pointers

In the previous lesson on expression parameters and template specialization, we took a look at a simple templated Storage class:

We showed that this class had problems when template parameter T was of type char* because of the shallow copy/pointer assignment that takes place in the constructor. In that lesson, we used full template specialization to create a specialized version of the Storage constructor for type char* that allocated memory and created an actual deep copy of tValue. For reference, here’s the fully specialized char* Storage constructor:

While that worked great for Storage<char*>, what about other pointer types? It’s fairly easy to see that if T is any pointer type, then we run into the problem of the constructor doing a pointer assignment instead of making an actual copy of the element being pointed to.

Because full template specialization forces us to fully resolve templated types, in order to fix this issue we’d have to define a new specialized constructor for each and every pointer type we wanted to use Storage with! This leads to lots of duplicate code, which as you well know by now is something we want to avoid as much as possible.

Fortunately, partial template specialization offers us a convenient solution. In this case, we’ll use class partial template specialization to define a special version of Storage that works for pointer values:

And an example of this working:

This prints the value:

7

The fact that we got a 7 here shows that cIntPtrStorage used the pointer version of Storage, which allocated it’s own copy of the int. If cIntPtrStorage had used the non-pointer version of Storage, it would have done a pointer assignment -- and when we changed the value of x, we would have changed cIntPtrStorage’s value too.

Using partial template class specialization to create separate pointer and non-pointer implementations of a class is extremely useful when you want a class to handle both differently, but in a way that’s completely transparent to the end-user.

15.1 -- The need for exceptions
Index
14.5 -- Class template specialization

10 comments to 14.6 — Partial template specialization

  • piciurica

    Hi All,

    Could someone, please, explain me this sentence: "If cIntPtrStorage had used the non-pointer version of Storage, it would have done a pointer assignment — and when we changed the value of x, we would have changed cIntPtrStorage’s value too." ?

    I don't see any pointer assignment in the non-pointer version of the Storage, thus I think that this sentence is confusion-prone. Besides, I run the code with non-pointer Storage version, and I still got 7, which I find logical, since in this version the parameters are passed by value.

    In order to change the value of x, the constructor of the pointer version of Storage should implement the pointer assignment instead of the new allocation. Am I right?

    Many thanks,
    Irina

  • DOBRESCU Mihai

    Hi,

    Strange behaviour in Code::Blocks. The sample program that was meant not to compile, it does compile. Here is the full code:


    #include <iostream>
    #include <cstring>

    using namespace std;

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

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

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

    template <typename T, int nSize>
    void PrintBufferString(Buffer<T, nSize> &rcBuf)
    {
    std::cout << rcBuf.GetBuffer() << std::endl;
    }

    void PrintBufferString(Buffer<char, 10> &rcBuf)
    {
    std::cout << rcBuf.GetBuffer() << std::endl;
    }

    int main()
    {
    Buffer<char, 10> cChar10Buffer;
    Buffer<char, 11> cChar11Buffer;

    strcpy(cChar10Buffer.GetBuffer(), "Ten456789");
    strcpy(cChar11Buffer.GetBuffer(), "Eleven7890");

    PrintBufferString(cChar10Buffer);
    PrintBufferString(cChar11Buffer); // this will not compile

    return 0;
    }

    The output is, as instructed, the following:


    Ten456789
    Eleven7890

    However, if we change the two setter lines as follows:


    strcpy(cChar10Buffer.GetBuffer(), "Ten4567890");
    strcpy(cChar11Buffer.GetBuffer(), "Eleven78901");

    Then the output is the following. Why?

    Eleven78901

    Also, the last example misses the "template<>" line before a template specialization. This is the full code:


    #include <iostream>
    #include <cstring>

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

    template <typename T>
    class Storage<T*> // this is specialization of Storage that works with pointer types
    {
    private:
    T* m_tValue;
    public:
    Storage(T* tValue) // for pointer type T
    {
    m_tValue = new T(*tValue);
    }

    ~Storage()
    {
    delete m_tValue;
    }

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

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

    int main()
    {
    // Declare a non-pointer Storage to show it works
    Storage<int> cIntStorage(5);

    // Declare a pointer Storage to show it works
    int x = 7;
    Storage<int*> cIntPtrStorage(&x);

    // If cIntPtrStorage did a pointer assignment on x,
    // then changing x will change cIntPtrStorage too
    x = 9;
    cIntPtrStorage.Print();

    return 0;
    }

  • vikas

    Hii Alex
    s there any concept of WRAPPER CLASS in C++, if yes then please give any idia how to use it. If u hav prepared any tutorial then please mention ur link.

    thank in advance

  • sandyleo26

    Hi, c++ standard doesn't allow partial specification for function template. Partial specification only available for class. So the PrintBufferString example is not a partial specification. It's function template OVERLOAD which mimic the behavior of partial specification. Please correct this.

  • mccp13

    please help, how would the Print function in Buffer class be implemented as template specialization for double.. would it be something like :

    template
    void Buffer::Print()
    {
    for( int nCount=0 ; nCount < nSize ; ++nCount )
    std::cout << std::scientific << m_atBuffer[nCount] << std::endl;
    }

    ??

  • Janis

    Is "int" a misspelling here?

    template<int nSize>
    void PrintBufferString(Buffer<char, nSize> &rcBuf)
    {
        std::cout << rcBuf.GetBuffer() << std::endl;
    }
  • george

    Is this

    m_tValue = new T(*tValue);

    an implicit initialization for the value pointed by m_tValue? In other words, is it equivalent to:

    m_tValue = new T;
    *m_tValue=*tValue;
  • george

    I don't understand why this function

    void PrintBufferString(Buffer<char, 10> &rcBuf)

    should take only a literal, and not an integer argument like this:

    void PrintBufferString(Buffer<char, int> &rcBuf)

    ?
    Is it because function templates can not take an expression parameter (in this case int)? But then

    Buffer<char, int>

    is just a parameter type to the function, and should be OK.

  • in the "Partial template specialization for pointers" section, the point of specializing a version for pointer type was to deal with the issue of shallow/deep copy on constructor call. However you could imagine the following (pointer to pointer):

    int x = 7;
    int *y = &x;
    Storage<int**> cIntPtrPtrStorage(&y);
    
  • in the first code:

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

    shouldn't the return value be T& instead of int& ?

    [ Yes, definitely. Thanks for the catch. -Alex ]

Leave a Reply

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">