-
Notifications
You must be signed in to change notification settings - Fork 0
/
class_templates.cpp
310 lines (237 loc) · 7.32 KB
/
class_templates.cpp
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
// Class templates
// By using class templates, you can implement container classes
// while the element type is still open
// the type of this class is Stack<T>, with T being a template parameter
// Thus, you have to use Stack<T> whenever you use the type of this class
// in a declaration except in cases where the template arguments can be deduced
// However, inside a class template using a class name not followed by template
// arguments represents the class with its template parameters as its arguments
// unlike nontemplate classes, you can't declare or define class
// templates inside functions or block scope
// In general, templates can only be defined in global/namespace scope or inside
// class declarations
// To use an object of a class template, until C++17 you must always specify the
// template arguments explicitly
// By declaring type Stack<int>, int is used as type T inside the class template
// The code is instantiated only for template(member) functions that are called.
// For class templates, member functions are instantiated only if they are used.
// An instantiated class template's type can be used just like any other type.
// You can qualify it with const or volatile or derive array and reference
// types from it
// You can also use it as part of type definition with typedef or using
void foo(Stack<int> const & s)
{
using IntStack = Stack<int>;
Stack<int> istack[10];
IntStack istack2[10];
}
#include <vector>
#include <cassert>
template<typename T>
class Stack
{
private:
std::vector<T> elems;
public:
void push(T const & elem);
void pop();
T const& top() const;
bool empty() const
{
return elems.empty();
}
Stack(Stack const&);
Stack& operator=(Stack const&);
};
template<typename T>
bool operator== (Stack<T> const& lhs, Stack<T> const& rhs)
{
}
template<typename T>
void Stack<T>::push(T const& elem)
{
elems.push_back(elem);
}
template<typename T>
void Stack<T>::pop()
{
assert(!elems.empty());
elems.pop_back();
}
template<typename T>
T const& Stack<T>::top() const
{
assert(!elems.empty());
return elems.back();
}
// Friends -----------------------------------------------------------
template<typename T>
class Stack1
{
friend std::ostream& operator<< (std::ostream& strm, Stack<T> const& s)
{
s.printOn(strm);
return strm;
}
};
template<typename T>
class Stack2 {
template<typename U>
friend std::ostream& operator<< (std::ostream&, Stack2<U> const&);
};
template<typename T>
class Stack3;
template<typename T>
std::ostream& operator<< (std::ostream&, Stack3<T> const&);
template<typename T>
class Stack3 {
friend std::ostream& operator<< <T> (std::ostream&, Stack<T> const &);
};
//-----------------------------------------------------------------
// Specializations of Class Templates -----------------------------
// You can specialize a class template for certain template parameters.
// Similar to the overloading of function templates, specializing class templates
// allows you to optimize implementations for certain types or to fix a misbehavior of certain
// types for an instantiation of the class template
// However if you specialize a class template, you must also specialize all member functions
// To specialize a class template, you have to declare the class with a leading template<>
// and a specification of the types for which the class template is specialized.
// The types are used as a template argument and must be specified directly following the name
// of the class
template<>
class Stack<std::string> {
};
// Any definition of a member function must be defined as an "ordinary" member function,
// whith each occurence of T being replaced by the specialized type:
void Stack<std::string>::push(std::string const & elem)
{
elems.push_back(elem);
}
// Partial Specialization ----------------------------------------
// You can provide special implementations for particular circumstances,
// but some template parameters must still be defined by the user
// For example, we can define a special implementation of class Stack<> for pointers
template<typename T>
class Stack<T*> {
private:
std::vector<T*> elems;
public:
void push(T*);
T* pop();
T* top() const;
bool empty() const {
return elems.empty();
}
};
template<typename T>
void Stack<T*>::push(T* elem)
{
elems.push_back(elem);
}
template<typename T>
void Stack<T*>::pop()
{
assert(!elems.empty());
T * p = elems.back();
return p;
}
template<typename T>
T* Stack<T*>::top() const
{
assert(!elems.empty());
return elems.back();
}
// Note again that the specialization might provide a (slightly) different interface
// Partial specialization with Multiple Parameters
template<typename T1, typename T2>
class MyClass {
};
// partial specialization: both template parameters have same type
template<typename T>
class MyClass<T,T> {
};
// patial specialization: second type is int
template<typename T>
class MyClass<T, int> {
};
// partial specialization: both template parameters are pointer types
template<typename T1, typename T2>
class MyClass<T1*, T2*> {
};
// If more than one partial specialization matches equally well, the declaration is
// ambiguous
MyClass<int, int> m;
MyClass<int*, int*> m;
// To resolve the second ambiguity, you could provide an additional partial specialization
// for pointers of the same type:
template<typename T>
class MyClass<T*, T*> {
};
// Default class template arguments
// As for function templates, you can define default values for class template parameters
template<typename T, typename Cont = std::vector<T>>
class Stack {
private:
Cont elems;
public:
void push(T const& elem);
void pop();
T const& top() const;
bool empty() const {
return elems.empty();
}
};
template<typename T, typename Cont>
void Stack<T, Cont>::push(T const& elem)
{
elems.push_back(elem);
}
template<typename T, typename Cont>
void Stack<T, Cont>::pop()
{
assert(!elems.empty();
elems.pop_back();
}
template<typename T, typename Cont>
T const& Stack<T,Cont>::top() const
{
assert(!elems.empty());
return elems.back();
}
// Note that we now have two template parameters, so each definition of a member function must
// be defined with these two parameters
// Type aliases
// 1. By using keyword typedef
typedef Stack<int> IntStack;
void foo(IntStack const& s);
IntStack istack[10];
// 2. By using the keyword using (C++11)
using IntStack = Stack<int>;
void foo(IntStack const& s);
IntStack istack[10];
template<typename T>
using DequeStack = Stack<T, std::deque<T>>;
// Alias template simply gives a new name to an existing type
// Note that, in general, templates can only be declared in global/namespace scope or inside class
// declarations
// Alias templates for Member Types
template<typename T>
using Invoke = typename T::type;
// Class Template Argument Deduction
// Since C++17 you can skip to define the templates argument explicitly,
// if the constructor is able to deduce all template parameters
Stack<int> intStack1;
Stack<int> intStack2 = intStack1; // OK in all versions
Stack intstack3 = intStack1; // OK since C++17
template<typename T>
class Stack {
private:
std::vector<T> elems;
public:
Stack() = default;
Stack(T const& elem) : elems({elem}) {}
};
int main()
{
Stack intStack = 0;
}