4 #include <initializer_list>
13 struct in_place_t { };
15 constexpr nullopt_t nullopt{};
16 constexpr in_place_t in_place{};
18 #define NOEXCEPT_AS(...) noexcept(noexcept(__VA_ARGS__))
21 /* Base storage struct for an optional. Defines a trivial destructor, for types
22 * that can be trivially destructed.
24 template<typename T, bool = std::is_trivially_destructible<T>::value>
25 struct optstore_base {
26 bool mHasValue{false};
32 constexpr optstore_base() noexcept { }
33 template<typename ...Args>
34 constexpr explicit optstore_base(in_place_t, Args&& ...args)
35 noexcept(std::is_nothrow_constructible<T, Args...>::value)
36 : mHasValue{true}, mValue{std::forward<Args>(args)...}
38 ~optstore_base() = default;
41 /* Specialization needing a non-trivial destructor. */
43 struct optstore_base<T, false> {
44 bool mHasValue{false};
50 constexpr optstore_base() noexcept { }
51 template<typename ...Args>
52 constexpr explicit optstore_base(in_place_t, Args&& ...args)
53 noexcept(std::is_nothrow_constructible<T, Args...>::value)
54 : mHasValue{true}, mValue{std::forward<Args>(args)...}
56 ~optstore_base() { if(mHasValue) al::destroy_at(std::addressof(mValue)); }
59 /* Next level of storage, which defines helpers to construct and destruct the
63 struct optstore_helper : public optstore_base<T> {
64 using optstore_base<T>::optstore_base;
66 template<typename... Args>
67 constexpr void construct(Args&& ...args) noexcept(std::is_nothrow_constructible<T, Args...>::value)
69 al::construct_at(std::addressof(this->mValue), std::forward<Args>(args)...);
70 this->mHasValue = true;
73 constexpr void reset() noexcept
76 al::destroy_at(std::addressof(this->mValue));
77 this->mHasValue = false;
80 constexpr void assign(const optstore_helper &rhs)
81 noexcept(std::is_nothrow_copy_constructible<T>::value
82 && std::is_nothrow_copy_assignable<T>::value)
86 else if(this->mHasValue)
87 this->mValue = rhs.mValue;
89 this->construct(rhs.mValue);
92 constexpr void assign(optstore_helper&& rhs)
93 noexcept(std::is_nothrow_move_constructible<T>::value
94 && std::is_nothrow_move_assignable<T>::value)
98 else if(this->mHasValue)
99 this->mValue = std::move(rhs.mValue);
101 this->construct(std::move(rhs.mValue));
105 /* Define copy and move constructors and assignment operators, which may or may
108 template<typename T, bool trivial_copy = std::is_trivially_copy_constructible<T>::value,
109 bool trivial_move = std::is_trivially_move_constructible<T>::value,
110 /* Trivial assignment is dependent on trivial construction+destruction. */
111 bool = trivial_copy && std::is_trivially_copy_assignable<T>::value
112 && std::is_trivially_destructible<T>::value,
113 bool = trivial_move && std::is_trivially_move_assignable<T>::value
114 && std::is_trivially_destructible<T>::value>
115 struct optional_storage;
117 /* Some versions of GCC have issues with 'this' in the following noexcept(...)
118 * statements, so this macro is a workaround.
120 #define _this std::declval<optional_storage*>()
122 /* Completely trivial. */
124 struct optional_storage<T, true, true, true, true> : public optstore_helper<T> {
125 using optstore_helper<T>::optstore_helper;
126 constexpr optional_storage() noexcept = default;
127 constexpr optional_storage(const optional_storage&) = default;
128 constexpr optional_storage(optional_storage&&) = default;
129 constexpr optional_storage& operator=(const optional_storage&) = default;
130 constexpr optional_storage& operator=(optional_storage&&) = default;
133 /* Non-trivial move assignment. */
135 struct optional_storage<T, true, true, true, false> : public optstore_helper<T> {
136 using optstore_helper<T>::optstore_helper;
137 constexpr optional_storage() noexcept = default;
138 constexpr optional_storage(const optional_storage&) = default;
139 constexpr optional_storage(optional_storage&&) = default;
140 constexpr optional_storage& operator=(const optional_storage&) = default;
141 constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
142 { this->assign(std::move(rhs)); return *this; }
145 /* Non-trivial move construction. */
147 struct optional_storage<T, true, false, true, false> : public optstore_helper<T> {
148 using optstore_helper<T>::optstore_helper;
149 constexpr optional_storage() noexcept = default;
150 constexpr optional_storage(const optional_storage&) = default;
151 constexpr optional_storage(optional_storage&& rhs) NOEXCEPT_AS(_this->construct(std::move(rhs.mValue)))
152 { if(rhs.mHasValue) this->construct(std::move(rhs.mValue)); }
153 constexpr optional_storage& operator=(const optional_storage&) = default;
154 constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
155 { this->assign(std::move(rhs)); return *this; }
158 /* Non-trivial copy assignment. */
160 struct optional_storage<T, true, true, false, true> : public optstore_helper<T> {
161 using optstore_helper<T>::optstore_helper;
162 constexpr optional_storage() noexcept = default;
163 constexpr optional_storage(const optional_storage&) = default;
164 constexpr optional_storage(optional_storage&&) = default;
165 constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
166 { this->assign(rhs); return *this; }
167 constexpr optional_storage& operator=(optional_storage&&) = default;
170 /* Non-trivial copy construction. */
172 struct optional_storage<T, false, true, false, true> : public optstore_helper<T> {
173 using optstore_helper<T>::optstore_helper;
174 constexpr optional_storage() noexcept = default;
175 constexpr optional_storage(const optional_storage &rhs) NOEXCEPT_AS(_this->construct(rhs.mValue))
176 { if(rhs.mHasValue) this->construct(rhs.mValue); }
177 constexpr optional_storage(optional_storage&&) = default;
178 constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
179 { this->assign(rhs); return *this; }
180 constexpr optional_storage& operator=(optional_storage&&) = default;
183 /* Non-trivial assignment. */
185 struct optional_storage<T, true, true, false, false> : public optstore_helper<T> {
186 using optstore_helper<T>::optstore_helper;
187 constexpr optional_storage() noexcept = default;
188 constexpr optional_storage(const optional_storage&) = default;
189 constexpr optional_storage(optional_storage&&) = default;
190 constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
191 { this->assign(rhs); return *this; }
192 constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
193 { this->assign(std::move(rhs)); return *this; }
196 /* Non-trivial assignment, non-trivial move construction. */
198 struct optional_storage<T, true, false, false, false> : public optstore_helper<T> {
199 using optstore_helper<T>::optstore_helper;
200 constexpr optional_storage() noexcept = default;
201 constexpr optional_storage(const optional_storage&) = default;
202 constexpr optional_storage(optional_storage&& rhs) NOEXCEPT_AS(_this->construct(std::move(rhs.mValue)))
203 { if(rhs.mHasValue) this->construct(std::move(rhs.mValue)); }
204 constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
205 { this->assign(rhs); return *this; }
206 constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
207 { this->assign(std::move(rhs)); return *this; }
210 /* Non-trivial assignment, non-trivial copy construction. */
212 struct optional_storage<T, false, true, false, false> : public optstore_helper<T> {
213 using optstore_helper<T>::optstore_helper;
214 constexpr optional_storage() noexcept = default;
215 constexpr optional_storage(const optional_storage &rhs) NOEXCEPT_AS(_this->construct(rhs.mValue))
216 { if(rhs.mHasValue) this->construct(rhs.mValue); }
217 constexpr optional_storage(optional_storage&&) = default;
218 constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
219 { this->assign(rhs); return *this; }
220 constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
221 { this->assign(std::move(rhs)); return *this; }
224 /* Completely non-trivial. */
226 struct optional_storage<T, false, false, false, false> : public optstore_helper<T> {
227 using optstore_helper<T>::optstore_helper;
228 constexpr optional_storage() noexcept = default;
229 constexpr optional_storage(const optional_storage &rhs) NOEXCEPT_AS(_this->construct(rhs.mValue))
230 { if(rhs.mHasValue) this->construct(rhs.mValue); }
231 constexpr optional_storage(optional_storage&& rhs) NOEXCEPT_AS(_this->construct(std::move(rhs.mValue)))
232 { if(rhs.mHasValue) this->construct(std::move(rhs.mValue)); }
233 constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
234 { this->assign(rhs); return *this; }
235 constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
236 { this->assign(std::move(rhs)); return *this; }
241 } // namespace detail_
243 #define REQUIRES(...) std::enable_if_t<(__VA_ARGS__),bool> = true
247 using storage_t = detail_::optional_storage<T>;
252 using value_type = T;
254 constexpr optional() = default;
255 constexpr optional(const optional&) = default;
256 constexpr optional(optional&&) = default;
257 constexpr optional(nullopt_t) noexcept { }
258 template<typename ...Args>
259 constexpr explicit optional(in_place_t, Args&& ...args)
260 NOEXCEPT_AS(storage_t{al::in_place, std::forward<Args>(args)...})
261 : mStore{al::in_place, std::forward<Args>(args)...}
263 template<typename U, REQUIRES(std::is_constructible<T, U&&>::value
264 && !std::is_same<std::decay_t<U>, al::in_place_t>::value
265 && !std::is_same<std::decay_t<U>, optional<T>>::value
266 && std::is_convertible<U&&, T>::value)>
267 constexpr optional(U&& rhs) NOEXCEPT_AS(storage_t{al::in_place, std::forward<U>(rhs)})
268 : mStore{al::in_place, std::forward<U>(rhs)}
270 template<typename U, REQUIRES(std::is_constructible<T, U&&>::value
271 && !std::is_same<std::decay_t<U>, al::in_place_t>::value
272 && !std::is_same<std::decay_t<U>, optional<T>>::value
273 && !std::is_convertible<U&&, T>::value)>
274 constexpr explicit optional(U&& rhs) NOEXCEPT_AS(storage_t{al::in_place, std::forward<U>(rhs)})
275 : mStore{al::in_place, std::forward<U>(rhs)}
277 ~optional() = default;
279 constexpr optional& operator=(const optional&) = default;
280 constexpr optional& operator=(optional&&) = default;
281 constexpr optional& operator=(nullopt_t) noexcept { mStore.reset(); return *this; }
282 template<typename U=T>
283 constexpr std::enable_if_t<std::is_constructible<T, U>::value
284 && std::is_assignable<T&, U>::value
285 && !std::is_same<std::decay_t<U>, optional<T>>::value
286 && (!std::is_same<std::decay_t<U>, T>::value || !std::is_scalar<U>::value),
287 optional&> operator=(U&& rhs)
290 mStore.mValue = std::forward<U>(rhs);
292 mStore.construct(std::forward<U>(rhs));
296 constexpr const T* operator->() const { return std::addressof(mStore.mValue); }
297 constexpr T* operator->() { return std::addressof(mStore.mValue); }
298 constexpr const T& operator*() const& { return mStore.mValue; }
299 constexpr T& operator*() & { return mStore.mValue; }
300 constexpr const T&& operator*() const&& { return std::move(mStore.mValue); }
301 constexpr T&& operator*() && { return std::move(mStore.mValue); }
303 constexpr explicit operator bool() const noexcept { return mStore.mHasValue; }
304 constexpr bool has_value() const noexcept { return mStore.mHasValue; }
306 constexpr T& value() & { return mStore.mValue; }
307 constexpr const T& value() const& { return mStore.mValue; }
308 constexpr T&& value() && { return std::move(mStore.mValue); }
309 constexpr const T&& value() const&& { return std::move(mStore.mValue); }
312 constexpr T value_or(U&& defval) const&
313 { return bool(*this) ? **this : static_cast<T>(std::forward<U>(defval)); }
315 constexpr T value_or(U&& defval) &&
316 { return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(defval)); }
318 template<typename ...Args>
319 constexpr T& emplace(Args&& ...args)
322 mStore.construct(std::forward<Args>(args)...);
323 return mStore.mValue;
325 template<typename U, typename ...Args>
326 constexpr std::enable_if_t<std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value,
327 T&> emplace(std::initializer_list<U> il, Args&& ...args)
330 mStore.construct(il, std::forward<Args>(args)...);
331 return mStore.mValue;
334 constexpr void reset() noexcept { mStore.reset(); }
338 constexpr optional<std::decay_t<T>> make_optional(T&& arg)
339 { return optional<std::decay_t<T>>{in_place, std::forward<T>(arg)}; }
341 template<typename T, typename... Args>
342 constexpr optional<T> make_optional(Args&& ...args)
343 { return optional<T>{in_place, std::forward<Args>(args)...}; }
345 template<typename T, typename U, typename... Args>
346 constexpr optional<T> make_optional(std::initializer_list<U> il, Args&& ...args)
347 { return optional<T>{in_place, il, std::forward<Args>(args)...}; }
353 #endif /* AL_OPTIONAL_H */