Search

13.5 — Function template specialization

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

Template specialization is one way to accomplish this.

Let’s take a look at a very simple template class:

The above code will work fine for many data types:

This prints:

5
6.7

Now, let’s say we want double values (and only double values) to output in scientific notation. To do so, we can use a function template specialization (sometimes called a full or explicit function template specialization) to create a specialized version of the print() function for type double. This is extremely simple: simply define the specialized function (if the function is a member function, do so 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:

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

The template <> tells the compiler that this is a template function, but that there are no template parameters (since in this case, we’re explicitly specifying all of the types). Some compilers may allow you to omit this, but it’s proper to include it.

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

5
6.700000e+000

Another example

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*:

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

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

In other words, this just does a pointer assignment (shallow copy)! As a result, m_value ends up pointing at the same memory location as string. When we delete string in main(), we end up deleting the value that m_value 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:

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

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

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.

Although the above examples have all used member functions, you can also specialize non-member template functions in the same way.

13.6 -- Class template specialization
Index
13.4 -- Template expression parameters

20 comments to 13.5 — Function template specialization

  • Help

    Hi Alex,
    I wounder after u delete string shouldnt u point it to nullptr? I mean on the other they are object, so when the object dies it will also die so u only have to delete the memory from heap ( destructor) but when u wanna show shallow copy and create string after u delete it, its a dangling point? cause when u do a shallow copy to a object and after that object dies that string will still be left as a dangling point, i am right? (i am aware we delete before the object dies, but either way)
    (sorry for being pointy just wanna make sure )

    Thanks in advance!:)

    • Alex

      Generally, you should set a pointer to nullptr after deleting it, unless that pointer is going out of memory immediately after being deleted anyway. This includes local variable pointers deleted at the end of functions, as well as member pointers deleted in the destructor.

      Keep in mind that setting a pointer to nullptr won’t set other pointers pointing to the same thing to nullptr, so setting a pointer to nullptr will _not_ save you from dangling pointers!

  • KnowMore

    Can you tell me how to specialize the printArray() For Doubles?
    Thanks In Advance ALEX ! 🙂

    • Alex

      This doesn’t work because you’re trying to partially specialize a function, which C++ doesn’t support (at least as of C++14). Partial template specialization only works for classes, not individual functions. I’m not sure of any way to address what you’re trying to do, other than define a partially specialized class StaticArray<double, size>.

      I updated lesson 13.7 with some information about how to solve this issue.

  • The Long

    Hello Alex.
    I tried this program but it did not very well. Every time I run it, I encounter an error message.
    Here is the code:

    and here is what it print out in the console:
    50            
    Hello         // The string I enter
    Hello         // The output string
    And this is the error message in a box I got after it finish printing. And it happens right after "return 0;" executed (before the main() block finish) and cause the program to crash:
    "HEAP CORRUPTION DETECTED: after Normal block (#195) at /* the address of STR */
    CRT detected that the application wrote to memory after end of heap buffer"
    I did manage the dynamic memory and do nothing to the HEAP. What could be the problem?

    • Alex

      You have an off-by-one error in your specialized char* constructor:

      for (int i = 0; i <= length; ++i) m_value[i] = value[i]; Since you already increased length by 1 to account for the null terminator, the <= should be <.

      • Help

        Hi,
        if i have understand things correct i find this error. U dont have any function get_value() in ur class! Also the in the destructor the m_value = nullptr; isn’t necessery as the object dies then m_value is destroyed! Also u got a dangling point on ur string as u only delete it and its not a part of object! after u delete[] string; u should string = nullptr; (its not a part of object so u have to do it by urself!)

  • Ola Sh

    Hello Alex,

    Thanks again for your really good tutorials. In your last example, should the strString pointer be deleted with delete[] since it is an array? Also, I tried to run the code below, but it keeps crashing at line 4 (cString.Print()). I have tried using Visual Studio debugging tools with no success. I would appreciate your help. Thanks. (The code works if I collect user input for the strString pointer).

    • Alex

      Updated the missing [] on delete.

      The problem here is that you’re assigning string literal “Hello World” to strString (which changes the address of strString) and then later trying to delete it. Since “Hello World” isn’t a dynamically allocated string, this causes a crash.

      You probably meant to do a strcpy_s here instead of an assignment.

  • Pavan

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

    Here m_tValue is Private member variable. How is it allowing us to use the value outside the class scope?

    • Alex

      It’s not outside the class scope. Storage::Print() is a member function of class Storage, so that function can directly access private members.

  • Reaversword

    You said to get a template specialization working, we need just redefine the function out of the class, with the concrete type we need to specialize the function, in this way:

    But, if we’re using a template expression parameter, as in "Buffer" example and we need to specialize a function, how can we do that to specialize the type, but not the nSize?

    I mean:

    basically is about mix two cases described in the lesson. It is possible redefine a function with a concrete type, but with any nSize?.

    • Alex

      It looks like C++ does not allow you to do partial template specialization of member functions (you can for non-member functions though).

      The solution is to partially specialize the whole class, so the member function isn’t partially specialized compared to the class it’s part of.

  • Kiran C K

    In the line,

    can the specific template’s number of type parameters be different than the generic one? Also, if we do not need the expression parameter, can we omit that in the specific version for a type?

    • Alex

      > can the specific template’s number of type parameters be different than the generic one?

      Not if they’re coming from the same template class declaration. You could do this with class template specialization though (which I cover in the next lesson).

      > Also, if we do not need the expression parameter, can we omit that in the specific version for a type?

      No, but you’re free to not use it.

  • Ashur

    Here is the code taken from this section that gives me an error message when compiled and run. Would appreciate any help in fixing my code. Thanks,
    ___________________________________________________
    #include <iostream>
    #include <cstring>
    #include <sstream>

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

    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*>::Storage(char* tValue)
          {
        m_tValue = new char[strlen(tValue)+1];
        strcpy(m_tValue, tValue);
          }

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

        // Delete the temporary string
        delete strString;

        // Print out our value
        strValue.Print();
    }
    _______________________________________________

    Error Message:
    example192.cpp: In function ‘int main()’:
    example192.cpp:40:28: error: expected primary-expression before ‘char’
         Storage<char>::Storage(char tValue)
                                ^
    example192.cpp:40:39: error: cannot call constructor ‘Storage<char>::Storage’ directly [-fpermissive]
         Storage<char>::Storage(char tValue)
                                           ^
    example192.cpp:40:39: note: for a function-style cast, remove the redundant ‘::Storage’
    example192.cpp:46:30: error: no matching function for call to ‘Storage<char*>::~Storage()’
         Storage<char*>::~Storage()
                                  ^
    example192.cpp:46:30: note: candidate is:
    example192.cpp:18:5: note: Storage<T>::~Storage() [with T = char*]
         ~Storage()
         ^
    example192.cpp:18:5: note:   candidate expects 1 argument, 0 provided
    example192.cpp:55:5: error: ‘strValue’ was not declared in this scope
         strValue.Print();

    • Alex

      A few thoughts:
      1) You have your specialized constructor and destructor inside main(). They need to be moved outside.
      2) Why use char* instead of std::string? std::string makes life easier.
      3) strValue was never defined.
      4) You never allocate an instance of your Storage class.

      Once you fix these issues you’ll be a lot closer to a working solution. 🙂

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter