C++ Library Extensions 2022.12.09
To help learn modern C++ programming
031-modern_cpp.cpp
Go to the documentation of this file.
1#include <iostream>
2#include <type_traits>
3#include <thread>
4#include <mutex>
5#include <stack>
6
7struct pod_type
8{
9 int m;
10};
11
13{
14 public:
15 int m;
16};
17
19{
20 std::cout << std::boolalpha;
21
22 std::cout << "Does a primitive type support a nonthrowing copy constructor? "
23 << std::is_nothrow_copy_constructible_v<int> << std::endl;
24
25 std::cout << "Does a primitive type support a nonthrowing move constructor? "
26 << std::is_nothrow_move_constructible_v<int> << std::endl;
27
28 std::cout << "Does a primitive type support a nonthrowing copy assignment? "
29 << std::is_nothrow_copy_assignable_v<int> << std::endl;
30
31 std::cout << "Does a primitive type support a nonthrowing move assignment? "
32 << std::is_nothrow_move_assignable_v<int> << std::endl << std::endl;
33
35 std::cout << "Does a pod type support a nonthrowing copy constructor? "
36 << std::is_nothrow_copy_constructible_v<pod_type> << std::endl;
37
38 std::cout << "Does a pod type support a nonthrowing move constructor? "
39 << std::is_nothrow_move_constructible_v<pod_type> << std::endl;
40
41 std::cout << "Does a pod type support a nonthrowing copy assignment? "
42 << std::is_nothrow_copy_assignable_v<pod_type> << std::endl;
43
44 std::cout << "Does a pod type support a nonthrowing move assignment? "
45 << std::is_nothrow_move_assignable_v<pod_type> << std::endl << std::endl;
46
48 std::cout << "Does an aggregate type support a nonthrowing copy constructor? "
49 << std::is_nothrow_copy_constructible_v<aggregate_type> << std::endl;
50
51 std::cout << "Does an aggregate type support a nonthrowing move constructor? "
52 << std::is_nothrow_move_constructible_v<aggregate_type> << std::endl;
53
54 std::cout << "Does an aggregate type support a nonthrowing copy assignment? "
55 << std::is_nothrow_copy_assignable_v<aggregate_type> << std::endl;
56
57 std::cout << "Does an aggregate type support a nonthrowing move assignment? "
58 << std::is_nothrow_move_assignable_v<aggregate_type> << std::endl << std::endl;
59}
60
62{
63 private:
64 bool m_invalid{false};
65
66 void cleanup()
67 {
68 if(this->m_invalid == false)
69 std::cout << "Destructor cleaned up" << std::endl;
70 }
71 public:
73 {
74 std::cout << "Default constructor called" << std::endl;
75 }
76
78 {
79 std::cout << "NonThrowingMovable(int v) called" << std::endl;
80 }
81
83 {
84 std::cout << "Copy constructor called" << std::endl;
85 }
86
88 {
89 std::cout << "Move constructor called" << std::endl;
90 rhs.m_invalid = true;
91 }
92
94 {
95 if(this != std::addressof(rhs))
96 {
97
98 }
99
100 std::cout << "Copy assignment called" << std::endl;
101
102 return *this;
103 }
104
106 {
107 if(this != std::addressof(rhs))
108 {
109 rhs.m_invalid = true;
110 }
111
112 std::cout << "Move assignment called" << std::endl;
113
114 return *this;
115 }
116
118 {
119 cleanup();
120 }
121};
122
123template<typename ElementType>
125{
126 private:
127 std::stack<ElementType> m_data;
128 mutable std::mutex m_mutex;
129
130 public:
132 { }
133
134 // void push(ElementType value)
135 // {
136 // std::lock_guard<std::mutex> lock(m_mutex);
137 // m_data.push(std::move(value));
138 // }
139
140 template<typename... Types>
141 void push(Types&&... args)
142 {
143 std::lock_guard<std::mutex> lock(m_mutex);
144 m_data.emplace(std::forward<Types>(args)...);
145 }
146
147 // no expensive copy constructor of ElementType,
148 // no copy assignment
149 // no move assignment
150 // named return value optimization kicks in
151 // only one move constructor, which does not allocate dynamic memory.
152 ElementType pop()
153 {
154 std::lock_guard<std::mutex> lock(m_mutex);
155 if(m_data.empty()) throw std::exception{};
156
157 // the real return statement occurs at this point.
158 //
159 // what if the type ElementType does not support MOVE constructor?
160 // Luckily, most of modern C++ Standard container classes
161 // support NON-THROWING MOVE constructor.
162 // Also, primitive types and POD, aggregate types also support
163 // non-throwing move constructor. So the chances of
164 // failure of the following line of code is very slim.
165
166 // Even if ElementType does not support move constructor,
167 // such that copy constructor kicks in, and the dynamic allocation
168 // failed, this pop() function still holds thanking to
169 // Named Return Value Optimization (and Copy Elision in some cases)
170 auto named_return_value = std::move(m_data.top());
171
172 // if an exception is thrown while copy-constructing
173 // named_return_value, "The Invariant of m_data"
174 // still maintained.
175
176 std::cout << "The address of named_return_value: "
177 << & named_return_value << std::endl;
178
179 // because, this m_data.pop() will not executed in the event of exception.
180 m_data.pop();
181
182 return named_return_value;
183
184 // When Named Return Value Optimization kicks in,
185 // the object name in the return statements only
186 // informs the C++ compiler of the object that is
187 // designated as return value.
188 }
189
190 // this function is redundant
191 bool empty()
192 {
193 std::lock_guard<std::mutex> lock(m_mutex);
194 return m_data.empty();
195 }
196};
197
199{
201
202 stack.push(1);
203
204 auto top = stack.pop();
205
206 std::cout << "The address of top: " << &top << std::endl;
207
208}
209
210int main()
211{
212 // test_nonthrowing_copy_move_constructors_and_assignment();
213
215}
std::mutex mutex
Definition: 022-mutex.cpp:12
void test_nonthrowing_copy_move_constructors_and_assignment()
int main()
void test_optimal_thread_safe_stack()
auto & cout
auto & endl
NonThrowingMovable & operator=(NonThrowingMovable &&rhs) noexcept
NonThrowingMovable(NonThrowingMovable &&rhs) noexcept
NonThrowingMovable(const NonThrowingMovable &rhs)
NonThrowingMovable & operator=(const NonThrowingMovable &rhs)
void push(Types &&... args)