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 std::strcpy 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 a StaticArray<char, 14>, 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

86 comments to 13.7 — Partial template specialization

  • Alek

    someone told me that when I'm doing an inheritance for template classes the derived class sends it's template parameters to the base class,but I dunno if he was right or not.he said something like this happens : using StaticArray=StaticArray_Base<T,size>.any Idea I really am  confused..

  • Alek

    Hey,I'm very confused with something.how are you giving m_array a size using a child class template parameter ? how is that possible here ? in inheritance we couldn't initialize member variables of base class with a derived class. I'm tlking about this snippet:

    would be thankful if you could clear me,thanks a lot.

    • nascardriver

      This creates a new type. It doesn't matter where the `size` argument comes from.

      The `using` alias you showed has nothing to do with this.

  • nao

    Hey ! Thanks for this great tutorial, i love it.
    For the partial specialization of print function, i have a compile error :

    Multiple definition of 'void print<char,14>(StaticArray<char,14>&)

    I compile with code block, compiler gnu gcc compiler.
    Do i have to check an option ?

  • One fact I find interesting is this. Maybe it's obvious but, while a partial specialization of a member function of a

    templated class

    doesn't work because it is interpreted as a partial specialization (forbidden) of function

    .

    Once you partially specialize the class into

    (using the StaticArray_Base above), the same function definition as above is now interpreted as the definition (with its own repetition of template declaration) of the template function

    , member of partially-specialized class

    .

    And therefore it's possible to write:

    which looks identical to a partial function specialization as above, but it's not. I think this could be useful to appreciate the different interpretations, despite the same appearance.

  • Hi, in last example on partial template with member functions, why do we need virtual functions at all? wouldn't a simple overlad of print() function in

    work as well? Is it maybe to make StaticArray_Base abstract (and thus non-instantiable)? Thanks.

    • nascardriver

      Good catch. I don't know why Alex chose to make `print()` virtual. It would only be useful if `StaticArray_Base` was meant to be used directly, which it isn't. I made `print()` non-virtual, thanks!

  • kavin

    I think line 49 under "Partial template specialization" should be,

    Only strcpy_s accept destination length argument.

    Also, in the last example, i am not able to understand how line 34 works.

    Did StaticArray_Base<double, size> take a double template parameter because StaticArray_Base is a template class of type <T, size> ?

    And why wouldn't the below code work ?

    We should be able to do something like this right?
    But i get this error for line 29:"a template argument list is not allowed in a declaration of a primary template." What does it mean ?
    And as a result line 53 wont work too .

    • nascardriver

      > strcpy
      Fixed, thanks!

      > Did StaticArray_Base take a double template parameter because StaticArray_Base is a template class of type ?
      Yes, and we want the `StaticArray_Base` portion of `StaticArray` to use `double`s.

      > We should be able to do something like this right?
      No, that defeats the purpose of using a template. You're not using the first template parameter (`double`) for anything, remove it.

      • kavin

        Thank you @nascardriver. I kinda get it but i wonder why it dosen't allow me to "declare a double buffer with room for 4 doubles" if i delete line 27-31 in the code of last example?
        Is it because when you use inheritance with a common base for templates, only the Derived classes are used for any kind of operation and the Base class exist only to serve as a base for the derived classes ? If so then,

        also must be using the class from line 27-31 for everything, except for printing using doubleArray.print(); which is done by the next derived class containing the override. Am i correct ?

  • AbraxasKnister

    Is there any need for explicitly defining the constructor in the StaticArray<>?

    In chapter 12 an entire lesson emphasises the need to make the destructors virtual if they're not protected. Why did StaticArray_Base<> violate this?

    • nascardriver

      > Is there any need for explicitly defining the constructor in the StaticArray<>?
      No. There was lesson that suggested always adding a constructor. That's not needed, constructor removed.

      > Why did StaticArray_Base<> violate this?
      I suppose Alex did pump up his warnings when he wrote this example. Fixed.

      Thanks for your continuous feedback!

      • AbraxasKnister

        I'm glad to help!

      • koe

        "In chapter 12 an entire lesson emphasises the need to make the destructors virtual if they're not protected. Why did StaticArray_Base<> violate this?"

        Going along with this, I believe 'StaticArray' and 'StaticArray<double, size>' should be made final, or given virtual (or protected) destructors.

        • nascardriver

          In the inheritance example, `StaticArray` has a virtual destructor, because its parent has a virtual destructor (All children of a class with a virtual destructor have a virtual destructor). In the other examples, I agree those classes should use `final`. If we update just this lesson, people will ask why the classes are `final` here but not in the other lessons, so we'd have update all lessons after chapter 12. This takes a lot of time that's better spent in other places. I'm personally not using `final`, but I'll try starting to do it to see how it goes.

  • Maximus Gladius

    Maybe there is duplicated code but isn't readability more important so in my opinion the code:

    is better than :

    ???

    • nascardriver

      > Maybe there is duplicated code but isn't readability more important
      No, duplicate code is terrible. It might not seem like that now, but imagine if that class has 100 member function. Maintaining such code is pretty much impossible.
      The common base class barely harms the readability. The are solutions entirely without specialization (via type traits).

  • masterOfNothing

    For some reason strcpy_s() is not working on Code::Block 17.12
    I had to use strcpy to get the codes above working.

    Is strcpy_s() only available in newer libraries?

Leave a Comment

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