19.6 — Partial template specialization for pointers

In previous lesson 19.3 -- Function 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 m_value. For reference, here’s the fully specialized char* Storage constructor and destructor:

While that worked great for Storage<char*>, what about other pointer types (such as int*)? 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 deep 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 (and destructor) 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 the Storage class that works for pointer values. This class is considered partially specialized because we’re telling the compiler that it’s only for use with pointer types, even though we haven’t specified the underlying type exactly.

And an example of this working:

This prints the value:


When myintptr is defined with an int* template parameter, the compiler sees that we have defined a partially specialized template class that works with any pointer type, and instantiates a version of Storage using that template. The constructor of that class makes a deep copy of parameter x. Later, when we change x to 9, the myintptr.m_value is not affected because it’s pointing at its own separate copy of the value.

If the partial template specialization class did not exist, myintptr would have used the normal (non-partially-specialized) version of the template. The constructor of that class does a shallow copy pointer assignment, which means that myintptr.m_value and x would be referencing the same address. Then when we changed the value of x to 9, we would have changed myintptr’s value too.

It’s worth noting that because this partially specialized Storage class only allocates a single value, for C-style strings, only the first character will be copied. If the desire is to copy entire strings, a specialization of the constructor (and destructor) for type char* can be fully specialized. The fully specialized version will take precedence over the partially specialized version. Here’s an example program that uses both partial specialization for pointers, and full specialization for char*:

This works as we expect:


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.

19.x -- Chapter 19 comprehensive quiz
19.5 -- Partial template specialization

78 comments to 19.6 — Partial template specialization for pointers

  • kanaetochi E okiyi

    Why did you use template <typename> in the pointer example rather than template <class>?

  • James

    Greetings! I don't believe the full specialization of the print() function is required in the last example, as the default template class version works with both static non-pointer types and dynamic C-style strings. Is this correct?


    • nascardriver


      The specialized `print()` function is different. The non-specialized version would print only the first character of the string.

      • James

        Hi nascardriver, thanks for your reply. I see the difference now, it's down to the single newline character that separates the two functions. I suppose it would depend on the requirement for a newline character in your function. I just assumed that a newline character would be omitted from the function and that it would be included in program using it if required, cheers.


        • nascardriver

          The newline doesn't matter. Remove the specialized version and you'll see the difference.

          • James

            I see what I did wrong now. I had the specialized constructor and destructor declared after the static type template class but before the dynamic pointer type class. Hence in all cases objects of type T with static members were instantiated, hence the print() function in the static type class was called.


  • Tim

    >>// If myintptr did a pointer assignment on x, then changing x will change myintptr too

    shouldn't the comment change? Because it turns out it doesn't work that way the comment stated  and myintptr doesn't change when x is modified.

  • Gustaw

    Is it possible to make our partial specialization class for pointers deep-copy the whole array of many types, for exapmle when we give it an int-type array, or we need to do it for each type the same way the char* full specialization was implemented?

  • rapunzel

    Hi Alex/nascar,

    I am trying to make the below code work but I get compilation errors. The idea is to use abstract class/polymorphism along with templates. I'm unsure what is going wrong here. Any ideas on how to make this work ?

    • nascardriver

      You're missing `class` or `typename` everywhere. Please always post the error message along with your code.

  • masterOfNothing

    I should be asking about templates, but what bothers me is that

    doesn't seem to work for me. I do have C++14 enabled.

    • Alex

      What happens when you try to do this?

      • masterOfNothing

        When I run the particular version of code, I get:

        C:\....cpp|88|error: invalid conversion from 'const char*' to 'char' [-fpermissive]|

        I use Code::Blocks 17.12. And in global compiler settings "have g++ follow the c14++ ISO ..." flag is activated.

        I remember in the lesson about dynamically allocating arrays this was mentioned as a problem.
        So instead it was advised to use strcpy() function to copy the string value into the char pointer.

        However, this lesson implies it is actually possible. I'm thinking whether the compiler that Code::Blocks uses is not up to the task.

    • sv

      same error with c++17 too

  • Atas

    You can do the partial specialization for const types in the same way, right?

    Are there common pitfalls about it?

    • Alex

      You mean like this?

      No pitfalls that I'm aware of.

  • Ryan

    How would I use templates to declare operator<< function??
    I tried this but I got compile error.

  • Hello Alex,
    is "partial template specialization for pointers" used also in standard classes such as vector, for example vector<int*> ?
    and if no is it okay for some cases such as this to have shallow copy so that this pointer is not pointing to a copied value but the member sent ?

  • Lenz

    Hey Alex,

    Thank you for such an amazing, free tutorial.

    you explain very well. I like it.

    Thank you,


  • Wilku

    Hi there,

    what if I'd like the user to be able to create the Storage class just for e.g. integer and double types but not for any other. Is there a way to provide such a limitation?

    Here is some code which has no sense but could illustrate what I would like to get.

    • Hi Wilku!

      You can use type traits (which are not covered on learncpp)

  • Nima

    Hello dear Alex.
    I have a question.
    Can we define a class or function that takes care of both passing new as function argument and a stack pointer?
    If we have a class or function with a pointer as its input and if we assume that user passing a heap object as function argument, we must use delete keyword inside body of function however if user passing a stack pointer to function, it must not delete otherwise the program will crash!
    Thanks in advance.

    • Hi Nima!

      The standard doesn't have any rules for a the use of heap or stack, your compiler doesn't even have to use them.

      If you still want to differentiate between heap and stack, you'll have to fall back to OS-specific functions to figure out where heap and stack are located and how big they are, then check if the passed pointer in inside on or the other region.

      But what about this?

      The first call shouldn't call delete. The second should. What about the third? You can't differentiate between the second and the third. If you call delete, @p will dangle. If you don't call delete, @p might leak when it's not deleted by the caller.

      • Nima

        Thank you so much.
        This is really weird.
        Now if I encounter with a generic function or public member function of a class that takes a pointer as input and can't access to its implementation how can I understand what type of pointer I should pass it (like your last example)?
        Really thanks.

  • kdart

    Just to be clear, with m_value = new T(*value) you are allocating memory for a pointer to T and initializing this memory address with whatever value points to, and then assigning it to m_value?

    And I think the sentence: "The constructor of that class does a shallow copy pointer assignment, which means that myintptr.m_value and x would be pointing to the same value." would be better as "The constructor of that class does a shallow copy pointer assignment, which means that myintptr.m_value and &x would be referencing the same address."  Since with both the old and new methods, myintptr.m_value and &x initially point to the same value.

    Sorry, I'm not trying to pick your nits.  I just love this site and I want to contribute!

    • nascardriver

      Hi kdart!

      > with m_value = new T(*value) you are allocating memory for a pointer to T and initializing this memory address with whatever value points to, and then assigning it to m_value?
      A new @T is created somewhere in memory and it's copy constructor is called with the object @value is pointing to. A pointer to the new @T is returned by @new and stored in @m_value.

    • Alex

      Thanks for the suggestion. I've integrated it into the lesson.

  • Dr. Aaron

    Got a bit confused here:

    "the compiler sees that we have defined a partially specialized template function that works with any pointer type"

    Question: the compiler sees specialized template function first or specialized template class?

    Thank you.

  • Lamont Peterson


    The first sentence in this lesson refers to "13.4 -- Template expression parameters", but that lesson does not include the Storage class or concepts mentioned in this lesson.  I think you meant that point at "13.5 -- Function template specialization".

    Also, I immediately noticed that the "Storage<char*>::Storage(char* value)" class we start off with here does not exactly match the one we ended up with in lesson 13.5.  The differences are minor and, ultimately, do not affect the functionality, but it's still a minor inconsistency.  Personally, I prefer using "!= '\0'" over "!= 0".  Indeed, I wondered why you chose the static integer comparison over the null character static char comparison at the time I was doing lesson 13.5.  Again, it doesn't affect functionality, but I like showing the intent of matching the null character '\0' as it is contextually a more correct thing to imply.

    • Alex

      Fixed, thanks. I probably copied the code from the previous lesson, made the change from 0 to '\0', then forgot to back-port the change to the previous lesson. I made them consistent now, and I agree, '\0' is slightly better at expressing intent that we're looking for a null terminator.

  • Gapo

    I even copy-pasted your code  just to be sure and it still wont compile and shows this error, do you have any idea what could be wrong ? I had problems using strcpy_s() in the previous lesson, the compiler could not find that function eventhough I included cstring and #define __STDC_WANT_LIB_EXT1__ 1

    • Alex

      It sounds like your compiler may not be C++14 compatible, which is needed to execute the line the compiler is complaining about. I've updated the example with some code that should compile for you. Give it a shot and let me know if it works.

    • Lamont Peterson


      Your error message looks to me like it came from g++.  I'm using clang++ 8.1.0 on macOS 10.12 and I got:

      For some reason, a 5 character string literal is too long to fit into a 40 character char array?  Obviously, no, it's not.  With some fiddling around, I was also getting some "error: cannot initialize a variable of type 'char *' with an rvalue of type 'char (*)[6]'" error messages (I even had some errors that said "lvalue" instead of "rvalue" in that same message), which is pretty similar in category to what you were getting.

      Therefore, I'm curious what compiler and OS you're on?

      So, I finally caved in and used strcpy, which works great.  Looking at this one line of code, I'm trying to figure out when I might ever be in this situation, anyway:

      Am I ever going to try and put static initializer C-style strings into dynamically allocated memory in C++ programming?  I think not.  I'll be using std::string or QString or some other string class and probably won't try to use "char* var = new char[40]" fixed-at-compile-time-dynamic-memory-allocated-arrays; ever.

  • Satwant

    if we do full initialization of char* type before partial initialization for T* type, it prints full name "Alex" even without that extra print function we defined for char* type.

    but vice versa is not same. then we need print function. can we try forward declaration here?

    • Alex

      I think in the case where the Storage<char*>::print() function is not defined, it will use the Storage<T>::print() function. In this case, that works out fine since std::cout knows how to handle printing objects of type char* (the implementation of the char* version is the same as the implementation of the generic T version, so the char* version is redundant).

      • Satwant

        • Alex

          It sounds like the compiler, not finding a print() function in Storage<char*> is using the one from T* instead, which dereferences the pointer. For a pointer pointing to an C-style string, dereferencing the pointer will return the value of the first char in the array.

      • nader

        hi alex,in above program i don't understand why "if we do full initialization of char* type before partial initialization for T* type, it prints full name "Alex",and use primary template,why don't use partial template that exist??"

  • nagarajsherigar

    i am trying to create an object of type double from an object of type int.Not getting how to do it

    • Alex

      I don't see where you're creating an object of type double or int. I assume test is supposed to be a Test<int> and test3 is supposed to be a Test<double>. If that's the case, then no, that's not allowed, because Test<double> doesn't have a constructor that accepts objects of type Test<int>

  • Rohit

    Hi Alex!
    Great Tutorials. Need some help.

    1) Storage<char*>::Storage(char* value) constructor belongs to which class, Storage or Storage<T*> ?
    2) Isn't the statement while(value[length] != 0) statement be while(value[length] != '') ?
    3) strcpy_s() showing error  "strcpy_s was not declared in the scope" even I included the headerfile <cstring>.

    • Alex

      1) Storage is the class the constructor belongs to.
      2) It's fine as is. The null terminator has ascii value 0. It's probably better to use '\0' instead to make the intent clearer though.
      3) I've updated the example to remove the strcpy_s since it was extraneous anyway.

  • The Long

    Hi Alex. Thank you for the amazing tutorial.
    Currently, I'm using VC 2015 and I can't compile the following code:
    template <typename T>
    class Storage<T*>       // got complain here
    the compiler always complains that the syntax is not right, that I'm using the old style formal list. Is there anyway to work around this?

    • Alex

      It works fine for me on VS2015. Make sure you included the base template definition for Storage above the specialized version, so the compiler knows what you're specializing.

  • Dominik

    In the last example, in line 108 there is a comment "//This will print garbage". Is this correct?

  • Trey

    Is it possible to have a default template like:

  • Mauricio Mirabetti

    Dear Alex,
    After your first example of code using Storage<int*>, there's an information missing:
    "This prints the value:
    5 // this is missing

    Also, on the example showing the use of full specialized Storage<char *> as well, you wrote:
        // If intptr7 did a pointer assignment on x,
      where it should be
          // If myintptr did a pointer assignment on x,
    Best regards.


  • 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,

    • Alex

      Yes, it's a little confusing, but I'm not sure how to word it any more clearly.

      Here's the definition for the non-pointer constructor of Storage

      If T is of type int*, then this becomes:

      And now this isn't a pass by value, but rather a pass by address, along with a pointer copy in the body of the constructor.

      This means that m_value would be pointing to the same value as the argument passed in. That means if we change the value of the argument, we're changing the value that m_value is pointing at too, and thus print() print that new value, not the original value.

      The partially specialized pointer version avoids this problem by dynamically allocating memory for a copy of the argument, so that any changes to the argument do not affect the value that m_value is pointing at.

      Is there a better way to express this?

      • Mauricio Mirabetti

        Alex, I understood the question of piciurica. I had to read it a few times to understand as well. If I may suggest, there are two paragraphs where some confusion can be made, depending on how you read it, so I'd write them more or less like this:

        "If T is any pointer type other than char*, then the non-pointer version of template is used (class Storage<T>), and we run into the problem of the constructor doing a pointer assignment instead of making an actual deep copy of the element being pointed to."

        "The fact that we got a 7 here shows that myintptr used the pointer version of Storage - as it was supposed to - which allocated its own copy of the int. If we hand't created a pointer version (class Storate <T*>), myintptr would have used the non-pointer version of Storage, and it would have done a pointer assignment -- and when we changed the value of x (x = 9;), we would have changed myintptr’s value too."

        Just a suggestion, thou.
        Best regards.


  • 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):

    • Alex

      You can delete member functions, but unfortunately there's no way to tell C++ to delete a partially specified template, so that it can't be instantiated.

      If you want to prevent someone from instantiating a template of a certain type, the best thing to do at current (as of C++14) is to provide a partial or full specialization for the template, and throw an assert in the constructor. For example, to prevent people from using Storage with objects of type T**, you could do this:

Leave a Comment

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