1/*
2 * Template Metaprogramming Demo
3 *
4 * Demonstrates:
5 * - Function templates and class templates
6 * - Template specialization
7 * - Variadic templates
8 * - SFINAE basics
9 * - C++20 concepts
10 * - Type traits
11 *
12 * Compile: g++ -std=c++20 -Wall -Wextra 06_templates.cpp -o templates
13 */
14
15#include <iostream>
16#include <vector>
17#include <string>
18#include <type_traits>
19#include <concepts>
20
21// ============ Function Templates ============
22template<typename T>
23T my_max(T a, T b) {
24 return (a > b) ? a : b;
25}
26
27// Template overloading
28template<typename T>
29T my_max(T a, T b, T c) {
30 return my_max(my_max(a, b), c);
31}
32
33void demo_function_templates() {
34 std::cout << "\n=== Function Templates ===\n";
35
36 std::cout << "my_max(10, 20) = " << my_max(10, 20) << "\n";
37 std::cout << "my_max(3.5, 2.1) = " << my_max(3.5, 2.1) << "\n";
38 std::cout << "my_max(10, 20, 15) = " << my_max(10, 20, 15) << "\n";
39
40 std::string s1 = "hello", s2 = "world";
41 std::cout << "my_max(\"hello\", \"world\") = " << my_max(s1, s2) << "\n";
42}
43
44// ============ Class Templates ============
45template<typename T>
46class Stack {
47private:
48 std::vector<T> data_;
49
50public:
51 void push(const T& value) {
52 data_.push_back(value);
53 }
54
55 void pop() {
56 if (!data_.empty()) {
57 data_.pop_back();
58 }
59 }
60
61 T top() const {
62 return data_.back();
63 }
64
65 bool empty() const {
66 return data_.empty();
67 }
68
69 size_t size() const {
70 return data_.size();
71 }
72};
73
74void demo_class_templates() {
75 std::cout << "\n=== Class Templates ===\n";
76
77 Stack<int> int_stack;
78 int_stack.push(10);
79 int_stack.push(20);
80 int_stack.push(30);
81
82 std::cout << "Int stack top: " << int_stack.top() << "\n";
83 std::cout << "Int stack size: " << int_stack.size() << "\n";
84
85 Stack<std::string> str_stack;
86 str_stack.push("hello");
87 str_stack.push("world");
88
89 std::cout << "String stack top: " << str_stack.top() << "\n";
90}
91
92// ============ Template Specialization ============
93template<typename T>
94class Printer {
95public:
96 static void print(const T& value) {
97 std::cout << "Generic: " << value << "\n";
98 }
99};
100
101// Full specialization for bool
102template<>
103class Printer<bool> {
104public:
105 static void print(const bool& value) {
106 std::cout << "Bool: " << (value ? "true" : "false") << "\n";
107 }
108};
109
110// Partial specialization for pointers
111template<typename T>
112class Printer<T*> {
113public:
114 static void print(T* value) {
115 std::cout << "Pointer: " << static_cast<void*>(value);
116 if (value) {
117 std::cout << " -> " << *value;
118 }
119 std::cout << "\n";
120 }
121};
122
123void demo_specialization() {
124 std::cout << "\n=== Template Specialization ===\n";
125
126 Printer<int>::print(42);
127 Printer<bool>::print(true);
128
129 int x = 100;
130 Printer<int*>::print(&x);
131}
132
133// ============ Variadic Templates ============
134// Base case
135void print() {
136 std::cout << "\n";
137}
138
139// Recursive case
140template<typename T, typename... Args>
141void print(T first, Args... rest) {
142 std::cout << first << " ";
143 print(rest...);
144}
145
146// Variadic sum using fold expression (C++17)
147template<typename... Args>
148auto sum(Args... args) {
149 return (args + ...);
150}
151
152void demo_variadic_templates() {
153 std::cout << "\n=== Variadic Templates ===\n";
154
155 std::cout << "print(1, 2, 3, \"hello\", 3.14): ";
156 print(1, 2, 3, "hello", 3.14);
157
158 std::cout << "sum(1, 2, 3, 4, 5) = " << sum(1, 2, 3, 4, 5) << "\n";
159 std::cout << "sum(1.5, 2.5, 3.0) = " << sum(1.5, 2.5, 3.0) << "\n";
160}
161
162// ============ SFINAE (Substitution Failure Is Not An Error) ============
163// Enable if T is integral
164template<typename T>
165typename std::enable_if<std::is_integral<T>::value, T>::type
166double_value(T value) {
167 return value * 2;
168}
169
170// Enable if T is floating point
171template<typename T>
172typename std::enable_if<std::is_floating_point<T>::value, T>::type
173double_value(T value) {
174 return value * 2.0;
175}
176
177void demo_sfinae() {
178 std::cout << "\n=== SFINAE ===\n";
179
180 std::cout << "double_value(10) = " << double_value(10) << "\n";
181 std::cout << "double_value(3.5) = " << double_value(3.5) << "\n";
182
183 // This would fail to compile:
184 // double_value("hello");
185}
186
187// ============ C++20 Concepts ============
188template<typename T>
189concept Numeric = std::integral<T> || std::floating_point<T>;
190
191template<typename T>
192concept Addable = requires(T a, T b) {
193 { a + b } -> std::convertible_to<T>;
194};
195
196template<Numeric T>
197T triple(T value) {
198 return value * 3;
199}
200
201template<Addable T>
202T add_three(T a, T b, T c) {
203 return a + b + c;
204}
205
206void demo_concepts() {
207 std::cout << "\n=== C++20 Concepts ===\n";
208
209 std::cout << "triple(10) = " << triple(10) << "\n";
210 std::cout << "triple(3.5) = " << triple(3.5) << "\n";
211
212 std::cout << "add_three(1, 2, 3) = " << add_three(1, 2, 3) << "\n";
213 std::cout << "add_three(1.5, 2.5, 3.0) = " << add_three(1.5, 2.5, 3.0) << "\n";
214
215 // These would fail to compile:
216 // triple("hello");
217 // add_three("a", "b", "c"); // strings are Addable but behave differently
218}
219
220// ============ Type Traits ============
221template<typename T>
222void analyze_type(T value) {
223 std::cout << "Type analysis for value: " << value << "\n";
224 std::cout << " is_integral: " << std::is_integral_v<T> << "\n";
225 std::cout << " is_floating_point: " << std::is_floating_point_v<T> << "\n";
226 std::cout << " is_pointer: " << std::is_pointer_v<T> << "\n";
227 std::cout << " is_const: " << std::is_const_v<T> << "\n";
228 std::cout << " is_arithmetic: " << std::is_arithmetic_v<T> << "\n";
229}
230
231void demo_type_traits() {
232 std::cout << "\n=== Type Traits ===\n";
233
234 analyze_type(42);
235 analyze_type(3.14);
236
237 int x = 10;
238 analyze_type(&x);
239}
240
241// ============ Template Template Parameters ============
242template<typename T, template<typename> class Container>
243class Wrapper {
244private:
245 Container<T> data_;
246
247public:
248 void add(const T& value) {
249 data_.push_back(value);
250 }
251
252 void print() const {
253 std::cout << " Container contents: ";
254 for (const auto& item : data_) {
255 std::cout << item << " ";
256 }
257 std::cout << "\n";
258 }
259};
260
261void demo_template_template() {
262 std::cout << "\n=== Template Template Parameters ===\n";
263
264 Wrapper<int, std::vector> wrapper;
265 wrapper.add(10);
266 wrapper.add(20);
267 wrapper.add(30);
268 wrapper.print();
269}
270
271// ============ Compile-Time Computation ============
272template<int N>
273struct Factorial {
274 static constexpr int value = N * Factorial<N - 1>::value;
275};
276
277template<>
278struct Factorial<0> {
279 static constexpr int value = 1;
280};
281
282// C++11 constexpr function
283constexpr int factorial(int n) {
284 return (n <= 1) ? 1 : n * factorial(n - 1);
285}
286
287void demo_compile_time() {
288 std::cout << "\n=== Compile-Time Computation ===\n";
289
290 std::cout << "Factorial<5>::value = " << Factorial<5>::value << "\n";
291 std::cout << "factorial(5) = " << factorial(5) << "\n";
292
293 // These are computed at compile time
294 constexpr int fact10 = factorial(10);
295 std::cout << "factorial(10) = " << fact10 << "\n";
296}
297
298// ============ Main ============
299int main() {
300 std::cout << "Template Metaprogramming Demo\n";
301 std::cout << "==============================\n";
302
303 demo_function_templates();
304 demo_class_templates();
305 demo_specialization();
306 demo_variadic_templates();
307 demo_sfinae();
308 demo_concepts();
309 demo_type_traits();
310 demo_template_template();
311 demo_compile_time();
312
313 std::cout << "\nAll demos completed!\n";
314 return 0;
315}