C++ – Partial / Explicit specialization

Welcome to the fourth article in our C++ meta-programming series. This is the first advanced topic we discuss here, so if you are not familiar already with template usages in C++, I highly recommend you to first read the previous articles in this series.

Previous article in series: C++ templates – Beginners most common issue
Next article in series: Templates Infinity Theory – From C++11 to C++20 – Part 1

specialization.what().why();

What is C++ Specialization && Why do we need it?

Template function/class specializations are the function/class definitions for specific template params. Example:

// Template function (the "pattern")
template <typename T>
void my_func(T val) {
    std::cout << val << std::endl;
}

// Function specialization for int type
template<>
void my_func<int>(int val) {
    std::cout << val << std::endl;
}

Specializations can be generated by compiler at compile-time, and can be customized. A specialization that we created, will stop the compiler from generating the sane specialization with the default definition. Assume we change our int specialization to the following:

// Function specialization for int type
template<>
void my_func<int>(int val) {
    std::cout << "An integer: " << val << std::endl;
}

int main() {
    my_func("5");
    my_func(5);
    return EXIT_SUCCESS;
}

The compiler first sees the const char* call, and start the instantiation process to generate const char* specialization for my_func:

template<>
void my_func<const char*>(const char* val) {
    std::cout << val << std::endl;
}

Then it moves to the int call, but it doesn’t generate a new int specialization, because it’s already exist. The final code result is:

// Template function (the "pattern")
template <typename T>
void my_func(T val) {
    std::cout << val << std::endl;
}

// Function specialization for int type
template<>
void my_func<int>(int val) {
    std::cout << "An integer: " << val << std::endl;
}

template<>
void my_func<const char*>(const char* val) {
    std::cout << val << std::endl;
}

int main() {
    my_func("5"); // Prints: 5
    my_func(5); // Prints: An integer: 5
    return EXIT_SUCCESS;
}

Why do we need it?

A template function is just a pattern that the compiler use to instantiate new functions/classes with the types we want. In other words: In order to call a template function/class we need it to be exists with the types we want, and this is the way the compiler does it.

As developers, we can use this ability to create a different algorithms/treats to different types. Let’s take for example and add function:

#include <string>

template <typename T>
T add(T num1, T num2) {
    return num1 + num2;
}

using namespace std::string_literals;

int main() {
    std::cout << add(1, 3) << std::endl; // 4
    std::cout << add(1.2, 3.3) << std::endl; // 4.5
    std::cout << add("1"s, "3"s) << std::endl; // 13 ??
    // ""s literal & literals will be discuss in a future post
    return EXIT_SUCCESS;
}

The compiler create the same algorithm for every specialization it creates, but it doesn’t see the difference between operator+ of numbers, and the one of strings. To get our desired result, we have to create our own specialization for string case:

template <>
std::string add(std::string n1, std::string n2) {
    return std::to_string(std::stod(n1) + std::stod(n2));
}

Now the results would be as expected also for add("1"s, "3"s).

std::set_difference(partial.begin(), partial.end(), explicit.begin(), explicit.end(), std::inserter(this->section, this->section.begin());

What are the differences between partial & explicit specialization?

What is partial specialization?

So far we saw mostly explicit (full) specializations, which means that in every specialization, all of the template parameters are specified. The explicit specialization is the final step/result of the instantiation process. Unlike explicit, partial specialization is a tool for the developer, that the compiler sees almost just like any other unspecialized template.

namespace math {
    template <size_t Base, size_t N>
    struct pow {
        static constexpr long long val = Base * pow<Base, N - 1>::val;
    };

    template <size_t Base>
    struct pow<Base, 0> { // Partial specialized class
        static constexpr long long val = 1;
    };
}

const my_highly_important_value = math::pow<2, 22>;

Remember this example? Actually here we have partial specialized template class. We have a partial specialized class pow, for case that we got some unknown/unspecialized Base, with specialized N = 0. For any Base with N = 0, the compiler will generate val = 1.

What are the differences?

So first of all, partial specialization is not the final form of the template class. Whenever we use partial specialization we remember that there are still “free template parameter/s” that we don’t know exactly what they will be only from watching the class declaration. Every partial specialized template class that will be used in our code, will trigger the instantiation process and the compiler will create an explicit specialization for it.

The second thing about partial specialization, is that it can’t be used in functions. Functions can be only explicitly specialized or unspecialized [Note: we’ll discuss about if constexpr in a future post].

The compiler will always use the most specialized template class declaration it can. For example:

// Unspecialized -- 1
template <typename T, template<typename, typename = std::allocator<T>> typename Cont>
class my_class {
private:
    Cont<T> container;
};

// Partial specialization [Cont = std::vector] -- 2
template <typename T> class my_class <T, std::vector> {
private:
    std::vector<T> container;
};

// Partial specialization [T = int] -- 3
template <template<typename T, typename = std::allocator<T>> typename Cont> class my_class <int, Cont> {
private:
    Cont<int> container;
    // T val; // Error! T can only be used inside the template-template declaration
};

// Explicit specialization [T = int, Cont = std::vector] -- 4
template <> class my_class <int, std::vector> {
private:
    std::vector<int> container;
};

int main() {
    my_class<double, std::list>   my_class1; // 1
    my_class<double, std::vector> my_class3; // 2
    my_class<int,    std::list>   my_class2; // 3
    my_class<int,    std::vector> my_class4; // 4
}

As the above example shows, the compiler will always prefer the explicit specialized class over the partial specialized. The specialization levels:
[T, Cont] < [int, Cont] == [T, std::vector] < [int, std::vector]

In the above case, if the explicit specialization doesn’t exist, the 4th line in main will produce an ambitious call compile-time error:

Ambiguous partial specializations of 'my_class<int, std::vector>' partial specialization matches [with T = int] partial specialization matches [with Cont = vector]

Conclusion

partial.why();

Partial Specialization – Why?

As we saw in the pow class example, one of the template parameters defined the behavior of the whole class value, in case of N = 0. A general definition for partial specialization usage would be:

To define a special behavior for a specific case, when some of the template params match a predict params, and some are not relevant for the special behavior.

explicit.why();

Explicit Specialization – Why?

Both us and the compiler need the explicit specialization:

Explicit specialization is the way the compiler ID a template instance type.

We need it to define a special behavior for a specific template specialization.

More posts in this series:

Leave a comment