File: templateparam2.yo

package info (click to toggle)
c%2B%2B-annotations 13.02.02-1
  • links: PTS, VCS
  • area: main
  • in suites: forky
  • size: 13,576 kB
  • sloc: cpp: 25,297; makefile: 1,523; ansic: 165; sh: 126; perl: 90; fortran: 27
file content (152 lines) | stat: -rw-r--r-- 7,725 bytes parent folder | download | duplicates (2)
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
141
142
143
144
145
146
147
148
149
150
151
152
Template hi(template template parameter) template parameters allow us to
specify a em(class template) as a template parameter. By specifying a class
template, it is possible to add a certain kind of behavior (called a
emi(policy)) to an existing class template.

To specify an allocation em(policy), rather than an allocation tt(type)
for the class tt(Storage) we rephrase its class template header:
definition starts as follows:
        verb(    template <typename Data, template <typename> class Policy>
    class Storage...)

The second template parameter is new. It is a em(template template
parameter). It has the following elements:
    itemization(
    it() The keyword tt(template), starting the template template parameter;
    it() The keyword tt(template) is followed (between angle brackets) by a
        list of template parameters that must be specified for the template
        template parameter. These parameters em(may) be given names, but names
        are usually omitted as those names cannot be used in subsequent
        template definitions. On the other hand, providing formal names may
        help the reader of the template to understand the kind of templates
        that must be specified with the template template parameter.
    it() Template template parameters must match, in numbers and types (i.e.,
        template type parameters, template non-type parameters, template
        template parameters) the template parameters that must be specified
        for the policy. This can be tricky, as some templates use default
        parameters that are hardly ever changed (like the allocation schemes
        for containers). When specifying template template parameters those
        default types are used. If they should be specifiable then they must
        be added to the template template parameter's types. In the following
        example tt(fun) can only use the default allocator, tt(gun) em(must)
        specify the allocator when defining tt(vd), and tt(hun) defines a
        default allocator type, offering the option to either specify the
        allocator type or use the default type:
            verbinsert(-as4 examples/vector1.cc)
        Alternatively, em(alias templates), introduced in section
        ref(ALIASES), can be used to define names for templates using
        predefined parameter types.
    it() Following the bracketed list the keyword ti(class) or tt(typename) 
        em(must) be specified. 
    it() All parameters may be provided with default arguments. This is shown
        in the next example of a hypothetical class template:
        verb(template <
    template <
        typename = std::string,
        int = 12,
        template <typename = int> class Inner = std::vector
    >
    class Policy
>
class Demo
{
   ...
};)

Here, the class template tt(Demo) expects a template template parameter
        named tt(Policy), expecting three template parameters: a template type
        parameter (by default tt(std::string)); a template non-type parameter
        (by default having value 12); and tt(Policy) itself expects a template
        template parameter, called tt(Inner), by default using an tt(int) as
        its template type parameter.
       )
    Policy classes are often an integral part of the class under
consideration. Because of this they are often deployed as base classes. In the
example the class tt(Policy) could be used as a base class of the class
tt(Storage).

    The policy operates on the class tt(Storage)'s data type. Therefore the
policy is informed about that data type as well. Our class tt(Storage) now
begins like this:
        verb(    template <typename Data, template <typename> class Policy>
    class Storage: public Policy<Data>)

This automatically allows us to use tt(Policy)'s members when implementing
the members of the class tt(Storage).

    Our home-made allocation classes do not really provide us with many useful
members. Except for the extraction operator they offer no immediate access to
the data. This can easily be repaired by adding more members. E.g., the class
tt(NewAlloc) could be augmented with operators allowing access to and
modification of stored data:
            verb(        operator Data &()   // optionally add a `const' member too
        {
            return *d_data;
        }
        NewAlloc &operator=(Data const &data)
        {
            *d_data = data;
        })

The other allocation classes could be given comparable members.

    Let's use the allocation schemes in some real code. The next
example shows how tt(Storage) can be defined using some data type and an
allocation scheme. We start out again with a class tt(Storage):
        verb(    template <typename Data, template <typename> class Allocate>
    class Storage: public std::vector<Data, Allocate<Data>>
    {};)

That's all we have to do. Note that tt(std::vector) formally has two
template parameters. The first one is the vector's data type, which is always
specified; the second one is the allocator used by the vector. Usually the
allocator is left unspecified (in which case the default STL allocator is
used), but here it is mentioned explicitly, allowing us to pass our own
allocation policy to tt(Storage).

All required functionality is inherited from the tt(vector) base class, while
the policy is `factored into the equation' using a template template
parameter. Here's an example showing how this is done:
        verb(    Storage<std::string, NewAlloc> storage;

    copy(istream_iterator<std::string>(cin), istream_iterator<std::string>(),
            back_inserter(storage));

    cout << "Element index 1 is " << storage[1] << '\n';
    storage[1] = "hello";

    copy(storage.begin(), storage.end(),
         ostream_iterator<NewAlloc<std::string> >(cout, "\n"));)

Since tt(Storage) objects are also tt(std::vector) objects the STL tt(copy)
function can be used in combination with the em(back_inserter) iterator to add
some data to the tt(storage) object. Its elements can be accessed and modified
using the index operator. Then tt(NewAlloc<std::string>) objects are inserted
into tt(cout) (also using the tt(copy) function).

    Interestingly, this is not the end of the story. Remember that our
intention was to create a class allowing us to specify the em(storage type) as
well. What if we don't want to use a tt(vector), but instead would like to use
a tt(list)?

    It's easy to change tt(Storage)'s setup so that a completely different
storage type can be used on request, like a tt(deque). To implement this, the
storage class is parameterized as well, using yet another template template
parameter:
        verb(    template <typename Data, template <typename> class AllocationPolicy,
              template <typename, typename> class Container = std::vector>
    class Storage: public Container<Data, AllocationPolicy<Data>>
    {};)

The earlier example using a tt(Storage) object can be used again without
requiring any modifications at all (except for the above redefinition). It
clearly can't be used with a tt(list) container, as the tt(list) lacks
tt(operator[]). Trying to do so is immediately recognized by the compiler,
producing an error if an attempt is made to use tt(operator[]) on, e.g., a
    tt(list).footnote(A complete example showing the definition of the
allocation classes and the class tt(Storage) as well as its use is provided in
the annotations()'s distribution in the file
tt(yo/advancedtemplates/examples/storage.cc).) A tt(list) container, however
can still be specified as the container to use. In that case a tt(Storage) is
implemented as a tt(list), offering tt(list)'s interface, rather than
tt(vector)'s interface, to its users.