File: specialization.yo

package info (click to toggle)
c%2B%2B-annotations 6.5.0-1
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 15,176 kB
  • ctags: 2,567
  • sloc: cpp: 14,411; makefile: 2,988; ansic: 165; perl: 90; sh: 29
file content (140 lines) | stat: -rw-r--r-- 7,279 bytes parent folder | download
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
    The initial tt(add()) template, defining two identically typed parameters
works fine for all types sensibly supporting tt(operator+()) and a copy
constructor. However, these assumptions are not always met. For example, when
tt(char *)s are used, neither the tt(operator+()) nor the copy constructor is
(sensibly) available. The compiler does not know this, and will try to
instantiate the simple template function
        verb(
    template <typename Type>
    Type add(Type const &t1, Type const &t2);
        )
    But it can't do so, since tt(operator+()) is not defined for pointers. In
situations like these it is clear that a match between the template's type
parameter(s) and the actually used type(s) is possible, but the standard
implementation is senseless or produces errors.

    To solve this problem a emi(template explicit specialization) may be
defined.  A template explicit specialization defines the template function for
which a generic definition already exists, using specific actual template type
parameters.

    In the abovementioned case an explicit specialization is required for a
tt(char const *), but probably also for a tt(char *) type. Probably, as the
compiler still uses the standard type-deducing process mentioned earlier. So,
when our tt(add()) template function is specialized for tt(char *) arguments,
then its return type em(must) also be a tt(char *), whereas it em(must) be a
tt(char const *) if the arguments are tt(char const *) values. In these cases
the template type parameter tt(Type) will be deduced properly. With tt(Type ==
char *), for example, the head of the instantiated function becomes:
        centt(char *add(char *const &t1, char *const &t2))
    If this is considered undesirable, an em(overloaded) version could be
designed expecting pointers. The following template function definition
expects two (tt(const)) pointers, and returns a non-const pointer:
        verb(
    template <typename T>
    T *add(T const *t1, T const *t2)
    {
        std::cout << "Pointers\n";
        return new T;
    }
        )
    But we might still not be where we want to be, as em(this) overloaded
version will now only accept pointers to constant tt(T) elements. Pointers to
non-const tt(T) elements will not be accepted. At first sight it may come as a
surprise that the compiler will not apply a qualification transformation. But
there's no need for the compiler to do so: when non-const pointers are used
the compiler will simply use the initial definition of the tt(add()) template
function expecting any two arguments of equal types.

    So do we have to define yet another overloaded version, expecting
non-const pointers? It is possible, but at some point it should become clear
that we're overshooting our goal. Like concrete functions and classes,
templates should have well-described purposes. Trying to add overloaded
template definitions to overloaded template definitions quickly turns the
template into a kludge. Don't follow this approach. A better approach is
probably to construct the template so that it fits its original purpose, make
allowances for the occasional specific case, and to describe its purpose
clearly in the template's documentation.

    Nevertheless, there may be situations where a template explicit
specialization may be worth considering. Two specializations for tt(const) and
non-tt(const) pointers to characters might be considered for our tt(add())
template function. Template explicit specializations are constructed as
follows:
    itemization(
    it() They start with the keyword tt(template).
    it() Next, an empty set of angle brackets is written. This indicates to
the compiler that there must be an em(existing) template whose prototype
matches the one we're about to define. If we err and there is no such template
        hi(template-id does not match template declaration)
then the compiler reports an error like:
        verb(
    error: template-id `add<char*>' for `char* add(char* const&, char*
           const&)' does not match any template declaration
        )
    it() Next the head of the function is defined, which must follow the same
syntax as a template explicit instantiation declaration (see
section ref(TEMPFUNEXDEC)): it must specify the correct returntype, function
name, template type parameter explicitations, as well as the function's
parameter list.
    it() The body of the function, definining the special implementation that
is required for the special actual template parameter types.
    )
    Here are two  explicit specializations for the template function
tt(add()), expecting tt(char *) and tt(char const *) arguments (note that the
tt(const) still appearing in the first template specialization is unrelated to
the specialized type (tt(char *)), but refers to the tt(const &) mentioned in
the original template's definition. So, in this case it's a reference to a
constant pointer to a tt(char), implying that the tt(char)s may be modified):
        verb(
    template <> char *add<char *>(char * const &p1,
                                        char * const &p2)
    {
        std::string str(p1);
        str += p2;
        return strcpy(new char[str.length() + 1], str.c_str());
    }

    template <> char const *add<char const *>(char const *const &p1,
                                        char const *const &p2)
    {
        static std::string str;
        str = p1;
        str += p2;
        return str.c_str();
    }
        )
    Template explicit specializations are normally included in the file
containing the other template function's implementations.

    A template explicit specialization can be declared in the usual way. I.e.,
by replacing its body with a semicolon.

    Note in particular how important the pair of i(angle brackets) are that
follow the tt(template) keyword when declaring a template explicit
specialization. If the angle brackets were omitted, we would have constructed
a
        i(template instantiation declaration).
    The compiler would silently process it, at the expense of a somewhat
longer compilation time.

    When declaring a template explicit specialization (or when using an
instantiation declaration) the
        hi(template explicit type specification: omitting)
    explicit specification of the template type parameters can be omitted if
the compiler is able to deduce these types from the function's arguments.  As
this is the case with the tt(char (const) *) specializations, they could also
be declared as follows:
        verb(
    template <> char const *add(char const *const &p1,
                                        char const *const &p2);
    template <> char const *add(char const *const &p1,
                                        char const *const &p2);
        )
    In addition, tt(template <>) could be omitted. However, this would remove
the template character from the declaration, as the resulting declaration is
now nothing but a plain function declaration. This is not an error: template
functions and non-template functions may overload each other. Ordinary
functions are not as restrictive as template functions with respect to allowed
type conversions. This could be a reason to overload a template with an
ordinary function every once in a while.