C++ is full of surprises (albeit not always good ones ). It is a well-known fact that you can provide explicit specialisations for function templates and class templates. But it was a total surprise to me that you can define explicit specialisations for non‐template members of class template without specialising the class template itself!
The following non‐template members of class template can be explicitly specialised:
- member function (either static or not);
- static data member;
- member enumeration (since C++11);
- member class.
Let’s see some examples:
When this can be useful? Whenever you need some conditional logic for you class template based on template parameters, but that logic takes only a small fraction of class code. For example, you may have a class containing a dozen of methods, but only a couple of them require behaviour specific to template arguments. In that case, it is not practical to define explicit specialisation for the whole class due to large amount of code duplication. The better solution is to define specialisations only for those specific methods.
Perhaps the most useful part of this feature is the ability to specialise member functions. Let’s take a look at a concrete example.
Implementing Generic RAII Wrapper for Resource Handles
Opaque resource handles are used in many OS‐level, networking, and database APIs. Usually such handles must be closed with some kind of close_handle function. When dealing with handles in C++ you almost always want to use RAII1 wrapper to avoid handle leaks. We can define such a wrapper as follows:
OsHandle is some common handle type in our hypothetical operating system which needs to be closed by
CloseOsHandle function (the real world example is Windows API
HANDLE type with corresponding
CloseHandle function). Example usage:
Now suppose we want to use
OsInternetHandle which requires specific
CloseOsInternetHandle function to be closed (this is also common in Windows API: for example, WinHttp
HINTERNET handle requires
WinHttpCloseHandle). We can specialise our
Handle class for
The problem is solved, but the cost is a lot of code duplication. As you can see, the only real difference between specialised and primary template is
Cleanup method. Let’s leave our
Handle class definition as is and define specialisation for
Cleanup member function instead:
This is much better for maintainability and works exactly like the class specialisation above.
Note that our handle wrapper is somewhat simplified for the purpose of the article. It is assumed that various
HandleType arguments are in fact different types, not an aliases for some built‐in type like
void*. For example, if our
OsInternetHandle are both defined as synonyms for
int, the above specialisation will not work. For that reason, in real life you almost certainly should apply type‐safe handle idiom to your wrapper class described in the recent isocpp.org post by Emil Ernerfeldt. Then you can specialise
Cleanup method on handle tag (which is effectively a form of tag dispatching2).
Although described in Section 14.7 [temp.spec] of the C++ ISO/IEC standard, this unobvious feature is poorly documented elsewhere. In fact, I’ve found only a couple of reference manuals mentioning it: