Search

13.7 — Partial template specialization

This lesson and the next are optional reading for those desiring a deeper knowledge of C++ templates. Partial template specialization is not used all that often (but can be useful in specific cases).

In lesson 13.4 -- Template non-type parameters, you learned how expression parameters could be used to parameterize template classes.

Let’s take another look at the Static Array class we used in one of our previous examples:

This class takes two template parameters, a type parameter, and an expression parameter.

Now, let’s say we wanted to write a function to print out the whole array. 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:

0 1 2 3

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

(We covered strcpy_s in lesson 6.6 -- C-style strings if you need a refresher)

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

H e l l o ,   w o r l d !

For non-char types, it makes sense to put a space between each array element, so they don’t run together. However, with a char type, it makes more sense to print everything run together as a C-style string, which our print() function doesn’t do.

So how can we fix this?

Template specialization to the rescue?

One might first think of using template specialization. The problem with full template specialization is that all template parameters must be explicitly defined.

Consider:

As you can see, we’ve now provided an overloaded print function for fully specialized StaticArray<char, 14>. Indeed, this prints:

Hello, world!

Although this solves the issue of making sure print() can be called with an StaticArray, it brings up another problem: using full template specialization means we have to explicitly define the length of the array this function will accept! Consider the following example:

Calling print() with char12 will call the version of print() that takes a StaticArray<T, size>, because char12 is of type StaticArray<char, 12>, and our overloaded print() will only be called when passed a StaticArray<char, 14>.

Although we could make a copy of print() that handles StaticArray<char, 12>, what happens when we want to call print() with an array size of 5, or 22? We’d have to copy the function for each different array size. That’s redundant.

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 specialize classes (but not individual functions!) where some, but not all, of the template parameters have been explicitly defined. For our challenge above, the ideal solution would be to have our overloaded print function work with StaticArray of type char, but leave the length expression parameter templated so it can vary as needed. Partial template specialization allows us to do just that!

Here’s our example with an overloaded print function that takes a partially specialized StaticArray:

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

Here’s a full program using this:

This prints:

Hello, world! Hello, mom!

Just as we expect.

Note that as of C++14, partial template specialization can only be used with classes, not template functions (functions must be fully specialized). Our void print(StaticArray<char, size> &array) example works because the print function is not partially specialized (it’s just an overloaded function using a class parameter that’s partially specialized).

Partial template specialization for member functions

The limitation on the partial specialization of functions can lead to some challenges when dealing with member functions. For example, what if we had defined StaticArray like this?

print() is now a member function of class StaticArray<T, int>. So what happens when we want to partially specialize print(), so that it works differently? You might try this:

Unfortunately, this doesn’t work, because we’re trying to partially specialize a function, which is disallowed.

So how do we get around this? One obvious way is to partially specialize the entire class:

This prints:

0 1 2 3 4 5
4.000000e+00 4.100000e+00 4.200000e+00 4.300000e+00

While it works, this isn’t a great solution, because we had to duplicate a lot of code from StaticArray<T, size> to StaticArray<double, size>.

If only there were some way to reuse the code in StaticArray<T, size> in StaticArray<double, size>. Sounds like a job for inheritance!

You might start off trying to write that code like this:

How do we reference StaticArray? We can’t.

Fortunately, there’s a workaround, by using a common base class:

This prints the same as above, but has significantly less duplicated code.

13.8 -- Partial template specialization for pointers
Index
13.6 -- Class template specialization

53 comments to 13.7 — Partial template specialization

  • Hi Alex!

    > gcc requires the StaticArray_Base<double, size>:: prefix in the above line.  Still not sure why.

    "This is because the template parent of a template class is not instantiated during the compilation pass that first examines the template."
    https://stackoverflow.com/a/6592617/9364954

  • Andi

    Hi Alex,
    This function:  

    Maybe access m_array via

    is nicer.

  • Roberto Sangineto

    T* getArray();

    Needs to be defined in the last example, otherwise it won't compile… at least in my case

  • sam

    Typo "// declare an char array" --> "// declare a char array"

  • sam

    There's a typo in one of your code snippets where you write "printArray()" instead of "print()":

    "print() is now a member function of class StaticArray<T, int>. So what happens when we want to partially specialize print(), so that it works differently? You might try this:

    "

    Should be:

    • Alex

      Thanks. I think maybe I gave that function a different name since it doesn't work, but that's easily remedied with a comment. Appreciate the feedback.

  • TC

    Hi Alex and Nas,

    Thanks for all of your helps.

    This question is related to IDE compiler error about strcpy_s. Sorry I don't have VS and CodeBlock.

    However I tested on both NetBeans and XCode(on the mac).

    Also make sure the flag -std=c++14 is set.

    No success for getting the compiler recognise strcpy_s.

    Any idea?

    • Hi TC!

      @strcpy_s is not a standard C++ function, don't use it. I'll leave a comment on the lesson in question to let Alex think about whether or not it should stay.
      If you want more security than @std::strcpy offers you can use @std::strncpy, which accepts a third "count" parameter to specify the length.

      References
      std::strncpy - https://en.cppreference.com/w/cpp/string/byte/strncpy

      • TC

        Hi Nas,

        I replace the code as you suggested. It works.

        The output: Hello, world!Hello, mom

        With no space in between the two phrases.

  • seriouslysupersonic

    Hi!

    Shouldn't we mark print() virtual @StaticArray_Base and override whenever we reimplement print() in a partially specialised class that inherits @StaticArray_Base?

  • Trevor29

    In all of the Hello, world! and also the Hello, mum! examples, the null terminator on the string is also sent to cout (as a separate char). I am not sure whether this would cause some unexpected issues in some applications, particularly if the standard output is redirected from the console.
    Trevor

  • Trevor29

    Typo - in your first Hello World example, you wrote that the code will print:
    H e l l o ,   W o r l d !
    but it will actually print:
    H e l l o ,   w o r l d !
    (The "w" has been capitalised by mistake.)
    Trevor

  • Frederico

    Alex,
    You mentioned that as of C++14, partial template specialization can only be used with classes, not template functions. Is there a particular reason for this?

    • Alex

      http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#229 says, "A major concern over the idea of partial specialization of function templates is that function templates can be overloaded, unlike class templates. Simply naming the function template in the specialization, as is done for class specialization, is not adequate to identify the template being specialized."

      It looks like they were intending to look into this further, but then never got around to it and then the issue was closed.

  • Tuttyfruty

    I can't understand the difference between specialised function vs overloaded function.  Can you give an example that is invalid for compiler but it would be a specialised function, which is not a member function of any classes,  if it was valid.

    • Alex

      Function overloading allows us to define functions that share a name but have different signatures. This only works for functions.

      Template specializing allows us to define code for a specific type parameter, but does not allow us to change the number of parameters. This works for both functions and classes, but only if you actually have a template class in the first place.

      Both perform a similar function: to allow us to reuse the name of something and attribute different behaviors to that variant.

  • Michael

    Hi Alex,
    Can you explain a bit more on

    what if we address it this way:

    • Alex

      It won't even compile. It makes sense -- if you tried to define an object of type StaticArray then after the type substitutions, it would be trying to inherit from itself.

  • Dr. Aaron

    what is printArray()?

  • Luhan

    There is an error at Partial template specialization for member functions :

  • Stefan Mayer-Popp

    "we want to call print() will an array size of 5, or 22??"

    -will +with 🙂

    "we want to call print() with an array size of 5, or 22?"

    nice tutorial! helped me a lot so far 🙂

  • Lamont Peterson

    Alex,

    In some of the examples, you started off with a "void print ()" function within the StaticArray class, then change it mid-stream to be "void printArray ()" instead.  Personally, I went with "void print ()" throughout, since "printArray" seems a bit redundant.

  • Saiyu

    Hi Alex,
    I got an error when compiling the "member function" version of printArray().
    // begin
    #include<iostream>
    template <class T, int size> // size is the expression parameter
    class StaticArray_Base
    {
    protected:
        // The expression parameter controls the size of the array
        T m_array[size];

    public:
        T* getArray();

        T& operator[](int index)
        {
            return m_array[index];
        }
        void printArray()
        {
            for (int i = 0; i < size; i++)
                std::cout << m_array[i];
            std::cout << "\n";
        }
    };

    template <class T, int size> // size is the expression parameter
    class StaticArray: public StaticArray_Base<T, size>
    {
    public:
        StaticArray()
        {

        }
    };

    template <int size> // size is the expression parameter
    class StaticArray<double, size>: public StaticArray_Base<double, size>
    {
    public:

        void printArray()
        {
            for (int i = 0; i < size; i++)
                std::cout << std::scientific << m_array[i] << " ";
            std::cout << "\n";
        }
    };

    int main()
    {
        // declare an integer array with room for 12 integers
        StaticArray<int, 6> intArray;

        // Fill it up in order, then print it backwards
        for (int count = 0; count < 6; ++count)
            intArray[count] = count;
        intArray.printArray();
        // declare a double buffer with room for 4 doubles
        StaticArray<double, 4> doubleArray;

        for (int count = 0; count < 4; ++count)
            doubleArray[count] = (4. + 0.1*count);
        doubleArray.printArray();

        return 0;
    }
    // end
    The error massage is
    //////////////////////
    main.cpp: In member function ‘void StaticArray<double, size>::printArray()’:
    main.cpp:42:36: error: ‘m_array’ was not declared in this scope
        std::cout << std::scientific << m_array[i] << " ";
                                        ^
    //////////////////////
    I can not figure out what is going wrong. Can you help?

  • Cunni

    Hi Alex,

    In the code following this line:

    "print() is now a member function of class StaticArray<T, int>. So what happens when we want to partially specialize print(), so that it works differently? You might try this:"

    you forgot a '/' to end the code tag. 🙂

  • Daniel

    In the code block following "Using templates, we might write something like this"

    Line 5: std::cout << array[0] << ' ';
    should be: std::cout << array[count] << ' ';

  • Mauricio Mirabetti

    Dear Alex,
    I believe the template parameters declaration is missing on this part of the code:

    If not, then I might have misunderstood the concept.
    Best regards.
    Mauricio

  • Himanshu

    // Override print() for fully specialized StaticArray<char, 14>
    void print(StaticArray<char, 14> &array)
    {
        for (int count = 0; count < size; ++count)
            std::cout << array[count];
    }

    In this, count<size is showing error. count<14 works.

  • Ritter G

    so in the public portion of this code is that a function pointer for getbuffer?

  • Ola Sh

    Hello Alex,

    Thanks again for your nice tutorials. While practicing with your examples, I noticed that Visual Studio C++ compiler could not print out the address of a character(char) variable or array. Does this have anything to do with how C++ stores char variables? Also , does C++ allow partial template specialization of class member functions. Please see my code below. I tried specializing the Storage class for char* using partial template specialization, but the code failed to compile. The compiler gave many errors when it reached the specialized constructor for char*. Thanks for your help.

    • Alex

      Visual Studio should be able to print the address of a char or array. For a char, you'll have to use the address-of operator (&). For an array, you can just print the array variable since it's already a pointer (or will decay into one).

      C++ does not support partial specialization of single member functions. You have to partially specialize the entire class.

  • Lokesh

    The second last example(code below) doesn't work for char* because the constructor

    allocates memory for a single character only. Although sometimes the programmer may want that but most times when dealing with char* type we mostly refer to strings as there is no point in using a pointer to point to a single character. Thus only the first character in the string(which is passed by the user) will be copied. We still have to do full template specialization for char*
    I think you should mention that in the lesson.

    • Lokesh

      I thought I should provide the code for it because there are a few subtle differences which are very important.

      Also note that we cannot use explicit specialization for Storage<T*>::Print() function when T is char* because it will print garbage since size of string is not known.

      • Alex

        I'm not sure I see offhand why Print() wouldn't print properly. m_tValue should be a copy of the original null-terminated tValue string. Therefore, it should print until it encounters the null terminator, which should appear at the end of the string.

        • Lokesh

          Yes, you are right. I forgot to mention that this problem occurs only with the templated Storage class(not the specialized version for char*) as it is given in the example code where we allocate a single character in the constructor.

    • Alex

      Fair point. I've added a note into the lesson.

  • george

    Is this

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

    • LW

      good question, I have the same thought... anyone can confirm this usage?

    • Steve

      Good question. This is implicit assignment like:

      which means you are correct.

Leave a Comment

Put all code inside code tags: [code]your code here[/code]