What is the purpose of Explicit Specialization?

I have tried Googling explicit specializations(function templates), but can't seem to warp my head around what the intended purpose of using it is.

Can somebody give me the gist of what it is. How it is mostly used, and any real world example. Thanks in advance.
Mostly used for code that can be reused. For example a sorting function:
Having to make a different one for sorting different data types can be tedious. However if the sort function is templatized, you can specify the data type you would like to sort and all you have to provide on your part is maybe a comparison method for the data type if one does not exist.

Another example is a hashtable; real life example being the stl map class. If you can only map integers to integers, this will mean having to make a different hashtable for any other data type you would like to store.

If you are not able to follow my ramble, here is a link: http://www.gotw.ca/publications/mill17.htm
@Smac89, thanks for the quick reply.

Couldn't I just use a function template instead of an explicit specialization instead.
Ahh you're right, I am getting the 2 mixed up
Explicit specializations are for when you want the template class to behave in a special manner for a specific type. An extreme example would be std::numeric_limits, which is specialized for every integer type to give the correct value based on that type.

A simpler example (though perhaps not the best) would be vector<bool>, which is specialized to compress the data into bits instead of storing full boolean variables.
As Zhuge has already said, template specialization is for when you want to provide an alternate implimentation for a specific type.

A good example stolen from Herb Sutter is an array sorter. A templated form of the function can be coded to handle all numeric types like int, double, char, ... using operator< (etc) to compare the values. It will also work with any class which, like std::string, has had operator< (etc) defined for it.

template<class T> void sort(Array<T>& v) { /*...*/ };

But when it comes to char* values, this generic sort algorithm would sort the strings based on their memory addresses rather than their lexical values. So you need to provide a specialization that sorts char* value using strcmp() (or similar) for the comparisons rather than operator< (etc.)

template<> void sort<char*>(Array<char*>&);

(And if you wanted to sort an array of char arrays, it would need another overload which not only used strcmp, but new how to swap array entries using strcpy.)

Of course, in practice you would specialize just the compare (predicate) rather than the whole of the function (as does the std::sort algorithm).

Note that for classes (but not functions) there is both explicit (or full) specialization and partial specialiation.

Also note the interplay between template specialization and overloading.

Andy

Template Specialization and Overloading
http://www.gotw.ca/gotw/049.htm

Partial Specializations and Explicit Specializations
http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=50

Differences between template specialization and overloading for functions?
http://stackoverflow.com/questions/1511935/differences-between-template-specialization-and-overloading-for-functions

Why Not Specialize Function Templates?
http://www.gotw.ca/publications/mill17.htm
Last edited on
If all you wanted was to have a function that specialized on a specific type, then why can you just use the type using a regular function. For example, @andywestken gave an example of a specialization that would focus on the char * type. If I know what type I want to use, then why can't I do something like this...

1
2
3
4
5
6
7
8
char * print(char *p, char *m);
.
.
.
char * print(char *p, char *m)
{

}


or if I wanted a sorting function for type char *, couldn't I just use a regular function.
Because you can't, it gives a compiler error. It doesn't know whether to use the template version or the normal version.

See andy's post below, he knows more than me.
Last edited on
You can use either with functions.

But you should not mix the two; if you need to specialize more than once, use a consistent approach.

And there are issues! See links I posted earlier, this one, and elsewhere for more details.

Specialize Function Templates vs Function Overload vs Class Specializing
http://stackoverflow.com/questions/994949/specialize-function-templates-vs-function-overload-vs-class-specializing

If I enable both the template specialization and the function overload, it's the overload that wins in this case.

Andy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include <iostream>
#include <cstring>
using namespace std;

// 0 = none, 1 = template, 2 = overload, 3 = both
#define USE_SPECIALIZATION  3

// http://en.wikipedia.org/wiki/Gnome_sort
template <typename TElem>
void gnomesort(TElem arr[], size_t N) {
    size_t i = 0;
    while (i < N) {
        if (i == 0 || arr[i - 1] <= arr[i]) {
            ++i;
        } else {
            TElem tmp = arr[i];
            arr[i]   = arr[i - 1];
            arr[--i] = tmp;
        }
    }
}

#if (USE_SPECIALIZATION == 1) || (USE_SPECIALIZATION == 3)

template<>
void gnomesort<const char*>(const char* arr[], size_t N) {
    size_t i = 0;
    while (i < N) {
        if (i == 0 || (strcmp(arr[i - 1], arr[i]) <= 0)) {
            ++i;
        } else {
            const char* tmp = arr[i];
            arr[i]   = arr[i - 1];
            arr[--i] = tmp;
        }
    }
}

#endif

#if ((USE_SPECIALIZATION == 2) || (USE_SPECIALIZATION == 3))

void gnomesort(const char* arr[], size_t N) {
    size_t i = 0;
    while (i < N) {
        if (i == 0 || (strcmp(arr[i - 1], arr[i]) <= 0)) {
            ++i;
        } else {
            const char* tmp = arr[i];
            arr[i]   = arr[i - 1];
            arr[--i] = tmp;
        }
    }
}

#endif

#if ((USE_SPECIALIZATION != 0) && (USE_SPECIALIZATION != 1) && (USE_SPECIALIZATION != 2) && (USE_SPECIALIZATION != 3))

#error USE_SPECIALIZATION must be defined to be 0, 1, 2, or 3

#endif

// PI rounded to 8 sig fig = 3.1415927

void test_int() {
    cout << "* * * test_int * * *" << endl;
    cout << endl;

    int data[] = {3, 1, 4, 1, 5, 9, 2, 7};

    gnomesort(data, sizeof(data)/sizeof(data[0]));

    cout << "data =";
    for(size_t i = 0; i < sizeof(data)/sizeof(data[0]); ++i) {
        std::cout << ' ' << data[i];
    }
    cout << endl;

    cout << endl;
}

void test_char_star() {
    cout << "* * * test_char_star * * *" << endl;
    cout << endl;

    const char* data[] = {"three", "one", "four", "one", "five", "nine", "two", "seven"};

    gnomesort(data, sizeof(data)/sizeof(data[0]));

    cout << "data =";
    for(size_t i = 0; i < sizeof(data)/sizeof(data[0]); ++i) {
        std::cout << ' ' << data[i];
    }
    cout << endl;

    cout << endl;
}

int main() {
    test_int();
    test_char_star();

    return 0;
}

Last edited on
Topic archived. No new replies allowed.