Template Specialization in C++ Without a General Case

How to be very specific with template specializations, omitting the generic case

Introduction

Template specialization in C++ is a way of describing to the compiler an explicit case of a template, given specific template arguments. For example, if you have a generic template that takes a type T, you can describe a specialization that outlines how the template works for a specific type passed as T: eg, an int or float.

Sometimes, we may only want a specific subset of specializations, and to exclude the general template case.

LinkedInTable of Contents

Why, and how to omit the general template case link icon

This technique can be useful for eg: when you need a wrapper to strictly prohibit accidentally copying something by value. In that case, we can omit defining the general case. The idea is to leave the general case out, and define only the permitted specializations. This will provide the effect of generating an error if the programmer uses a type outside of the specializations described in the code. Diagnostics can be improved in C++20 with the use of Concepts (not shown here).

We do this by forward declaring the general case, and omitting the actual definition of it.

\In this example, the compiler generates code only for pointers and reference types

/*
    Template specialization, excluding the general case
    - the template does not exist in the general case; only the specialisations
    - trying to instantatate a not specialized case will throw an error
 */

// fwd. declaration, omits the generic template
template <typename T>
class PRefOnlyValue;

// specialization to support pointer types
template <typename T>
class PRefOnlyValue<T*> {
public:
    explicit PRefOnlyValue(T* p) : v(*p) { }
private:
    T v;
};

// specialization to support reference types
template <typename T>
class PRefOnlyValue<T&> {
public:
    explicit PRefOnlyValue(T& p) : v(p) { }
private:
    T v;
};

void main() {
    int i = 5;
    int* p = &i;
    int& ref = i;
    PRefOnlyValue<int*> v1(p);
    PRefOnlyValue<int&> v2(ref);
    // PRefOnlyValue<int> v3(i);   // this will not work.
}

Comments