fix util::optional comparison operators
[kazan.git] / src / util / optional.h
1 /*
2 * Copyright 2017 Jacob Lifshay
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 *
22 */
23
24 #ifndef UTIL_OPTIONAL_H_
25 #define UTIL_OPTIONAL_H_
26
27 #include <type_traits>
28 #include <new>
29 #include <memory>
30 #include <initializer_list>
31 #include <utility>
32 #include <cassert>
33 #include <exception>
34 #include <functional>
35 #include "in_place.h"
36 #include "is_swappable.h"
37
38 namespace kazan
39 {
40 namespace util
41 {
42 struct nullopt_t
43 {
44 constexpr explicit nullopt_t(int)
45 {
46 }
47 };
48
49 constexpr nullopt_t nullopt(0);
50
51 class bad_optional_access : public std::exception
52 {
53 public:
54 virtual const char *what() const noexcept override
55 {
56 return "bad_optional_access";
57 }
58 };
59
60 namespace detail
61 {
62 template <typename T,
63 bool Is_Trivially_Destructible = std::is_trivially_destructible<T>::value,
64 bool Is_Trivially_Copyable = std::is_trivially_copyable<T>::value>
65 struct Optional_base
66 {
67 union
68 {
69 T full_value;
70 alignas(T) char empty_value[sizeof(T)];
71 };
72 bool is_full;
73 constexpr Optional_base() noexcept : empty_value{}, is_full(false)
74 {
75 }
76 constexpr Optional_base(nullopt_t) noexcept : empty_value{}, is_full(false)
77 {
78 }
79 void reset() noexcept
80 {
81 if(is_full)
82 full_value.~T();
83 is_full = false;
84 }
85 template <typename... Types>
86 T &emplace(Types &&... args) noexcept(std::is_nothrow_constructible<T, Types...>::value)
87 {
88 reset();
89 ::new(static_cast<void *>(std::addressof(full_value))) T(std::forward<Types>(args)...);
90 is_full = true;
91 return full_value;
92 }
93 template <
94 typename U,
95 typename... Types,
96 typename = typename std::
97 enable_if<std::is_constructible<T, std::initializer_list<U>, Types...>::value>::type>
98 T &emplace(std::initializer_list<U> init_list, Types &&... args) noexcept(
99 std::is_nothrow_constructible<T, std::initializer_list<U>, Types...>::value)
100 {
101 reset();
102 ::new(static_cast<void *>(std::addressof(full_value)))
103 T(init_list, std::forward<Types>(args)...);
104 is_full = true;
105 return full_value;
106 }
107 Optional_base(const Optional_base &rt) noexcept(std::is_nothrow_copy_constructible<T>::value)
108 : empty_value{}, is_full(false)
109 {
110 if(rt.is_full)
111 emplace(rt.full_value);
112 }
113 Optional_base(Optional_base &&rt) noexcept(std::is_nothrow_move_constructible<T>::value)
114 : empty_value{}, is_full(false)
115 {
116 if(rt.is_full)
117 emplace(std::move(rt.full_value));
118 }
119 template <typename... Types,
120 typename = typename std::enable_if<std::is_constructible<T, Types...>::value>::type>
121 constexpr explicit Optional_base(in_place_t, Types &&... args) noexcept(
122 std::is_nothrow_constructible<T, Types...>::value)
123 : full_value(std::forward<Types>(args)...), is_full(true)
124 {
125 }
126 template <
127 typename U,
128 typename... Types,
129 typename = typename std::
130 enable_if<std::is_constructible<T, std::initializer_list<U>, Types...>::value>::type>
131 constexpr explicit Optional_base(
132 in_place_t,
133 std::initializer_list<U> init_list,
134 Types &&... args) noexcept(std::is_nothrow_constructible<T, Types...>::value)
135 : full_value(init_list, std::forward<Types>(args)...), is_full(true)
136 {
137 }
138 ~Optional_base()
139 {
140 reset();
141 }
142 Optional_base &operator=(const Optional_base &rt) noexcept(
143 std::is_nothrow_copy_assignable<T>::value)
144 {
145 if(!rt.is_full)
146 reset();
147 else if(!is_full)
148 emplace(rt.full_value);
149 else
150 full_value = rt.full_value;
151 return *this;
152 }
153 Optional_base &operator=(Optional_base &&rt) noexcept(std::is_nothrow_move_assignable<T>::value)
154 {
155 if(!rt.is_full)
156 reset();
157 else if(!is_full)
158 emplace(std::move(rt.full_value));
159 else
160 full_value = std::move(rt.full_value);
161 return *this;
162 }
163 };
164
165 template <typename T>
166 struct Optional_base<T, true, false>
167 {
168 union
169 {
170 T full_value;
171 alignas(T) char empty_value[sizeof(T)];
172 };
173 bool is_full;
174 constexpr Optional_base() noexcept : empty_value{}, is_full(false)
175 {
176 }
177 constexpr Optional_base(nullopt_t) noexcept : empty_value{}, is_full(false)
178 {
179 }
180 void reset() noexcept
181 {
182 // full_value.~T() not needed
183 is_full = false;
184 }
185 template <typename... Types>
186 T &emplace(Types &&... args) noexcept(std::is_nothrow_constructible<T, Types...>::value)
187 {
188 reset();
189 ::new(static_cast<void *>(std::addressof(full_value))) T(std::forward<Types>(args)...);
190 is_full = true;
191 return full_value;
192 }
193 template <
194 typename U,
195 typename... Types,
196 typename = typename std::
197 enable_if<std::is_constructible<T, std::initializer_list<U>, Types...>::value>::type>
198 T &emplace(std::initializer_list<U> init_list, Types &&... args) noexcept(
199 std::is_nothrow_constructible<T, std::initializer_list<U>, Types...>::value)
200 {
201 reset();
202 ::new(static_cast<void *>(std::addressof(full_value)))
203 T(init_list, std::forward<Types>(args)...);
204 is_full = true;
205 return full_value;
206 }
207 Optional_base(const Optional_base &rt) noexcept(std::is_nothrow_copy_constructible<T>::value)
208 : empty_value{}, is_full(false)
209 {
210 if(rt.is_full)
211 emplace(rt.full_value);
212 }
213 Optional_base(Optional_base &&rt) noexcept(std::is_nothrow_move_constructible<T>::value)
214 : empty_value{}, is_full(false)
215 {
216 if(rt.is_full)
217 emplace(std::move(rt.full_value));
218 }
219 template <typename... Types,
220 typename = typename std::enable_if<std::is_constructible<T, Types...>::value>::type>
221 constexpr explicit Optional_base(in_place_t, Types &&... args) noexcept(
222 std::is_nothrow_constructible<T, Types...>::value)
223 : full_value(std::forward<Types>(args)...), is_full(true)
224 {
225 }
226 template <
227 typename U,
228 typename... Types,
229 typename = typename std::
230 enable_if<std::is_constructible<T, std::initializer_list<U>, Types...>::value>::type>
231 constexpr explicit Optional_base(
232 in_place_t,
233 std::initializer_list<U> init_list,
234 Types &&... args) noexcept(std::is_nothrow_constructible<T, Types...>::value)
235 : full_value(init_list, std::forward<Types>(args)...), is_full(true)
236 {
237 }
238 ~Optional_base() = default;
239 Optional_base &operator=(const Optional_base &rt) noexcept(
240 std::is_nothrow_copy_assignable<T>::value)
241 {
242 if(!rt.is_full)
243 reset();
244 else if(!is_full)
245 emplace(rt.full_value);
246 else
247 full_value = rt.full_value;
248 return *this;
249 }
250 Optional_base &operator=(Optional_base &&rt) noexcept(std::is_nothrow_move_assignable<T>::value)
251 {
252 if(!rt.is_full)
253 reset();
254 else if(!is_full)
255 emplace(std::move(rt.full_value));
256 else
257 full_value = std::move(rt.full_value);
258 return *this;
259 }
260 };
261
262 template <typename T>
263 struct Optional_base<T, true, true>
264 {
265 union
266 {
267 T full_value;
268 alignas(T) char empty_value[sizeof(T)];
269 };
270 bool is_full;
271 constexpr Optional_base() noexcept : empty_value{}, is_full(false)
272 {
273 }
274 constexpr Optional_base(nullopt_t) noexcept : empty_value{}, is_full(false)
275 {
276 }
277 void reset() noexcept
278 {
279 // full_value.~T() not needed
280 is_full = false;
281 }
282 template <typename... Types>
283 T &emplace(Types &&... args) noexcept(std::is_nothrow_constructible<T, Types...>::value)
284 {
285 reset();
286 ::new(static_cast<void *>(std::addressof(full_value))) T(std::forward<Types>(args)...);
287 is_full = true;
288 return full_value;
289 }
290 template <
291 typename U,
292 typename... Types,
293 typename = typename std::
294 enable_if<std::is_constructible<T, std::initializer_list<U>, Types...>::value>::type>
295 T &emplace(std::initializer_list<U> init_list, Types &&... args) noexcept(
296 std::is_nothrow_constructible<T, std::initializer_list<U>, Types...>::value)
297 {
298 reset();
299 ::new(static_cast<void *>(std::addressof(full_value)))
300 T(init_list, std::forward<Types>(args)...);
301 is_full = true;
302 return full_value;
303 }
304 constexpr Optional_base(const Optional_base &rt) noexcept = default;
305 constexpr Optional_base(Optional_base &&rt) noexcept = default;
306 template <typename... Types,
307 typename = typename std::enable_if<std::is_constructible<T, Types...>::value>::type>
308 constexpr explicit Optional_base(in_place_t, Types &&... args) noexcept(
309 std::is_nothrow_constructible<T, Types...>::value)
310 : full_value(std::forward<Types>(args)...), is_full(true)
311 {
312 }
313 template <
314 typename U,
315 typename... Types,
316 typename = typename std::
317 enable_if<std::is_constructible<T, std::initializer_list<U>, Types...>::value>::type>
318 constexpr explicit Optional_base(
319 in_place_t,
320 std::initializer_list<U> init_list,
321 Types &&... args) noexcept(std::is_nothrow_constructible<T, Types...>::value)
322 : full_value(init_list, std::forward<Types>(args)...), is_full(true)
323 {
324 }
325 ~Optional_base() = default;
326 Optional_base &operator=(const Optional_base &rt) noexcept = default;
327 Optional_base &operator=(Optional_base &&rt) noexcept = default;
328 };
329 }
330
331 template <typename T>
332 class optional;
333
334 namespace detail
335 {
336 template <typename T, typename U, typename U_Ref>
337 constexpr bool optional_needs_conversion_constructors() noexcept
338 {
339 if(!std::is_constructible<T, U_Ref>::value)
340 return false;
341 if(std::is_constructible<T, optional<U> &>::value)
342 return false;
343 if(std::is_constructible<T, const optional<U> &>::value)
344 return false;
345 if(std::is_constructible<T, optional<U> &&>::value)
346 return false;
347 if(std::is_constructible<T, const optional<U> &&>::value)
348 return false;
349 if(std::is_convertible<optional<U> &, T>::value)
350 return false;
351 if(std::is_convertible<const optional<U> &, T>::value)
352 return false;
353 if(std::is_convertible<optional<U> &&, T>::value)
354 return false;
355 if(std::is_convertible<const optional<U> &&, T>::value)
356 return false;
357 return true;
358 }
359
360 template <typename T, typename U, typename U_Ref>
361 constexpr bool optional_needs_conversion_from_optional_assign_operators() noexcept
362 {
363 if(!std::is_constructible<T, U_Ref>::value)
364 return false;
365 if(!std::is_assignable<T &, U_Ref>::value)
366 return false;
367 if(std::is_constructible<T, optional<U> &>::value)
368 return false;
369 if(std::is_constructible<T, const optional<U> &>::value)
370 return false;
371 if(std::is_constructible<T, optional<U> &&>::value)
372 return false;
373 if(std::is_constructible<T, const optional<U> &&>::value)
374 return false;
375 if(std::is_convertible<optional<U> &, T>::value)
376 return false;
377 if(std::is_convertible<const optional<U> &, T>::value)
378 return false;
379 if(std::is_convertible<optional<U> &&, T>::value)
380 return false;
381 if(std::is_convertible<const optional<U> &&, T>::value)
382 return false;
383 if(std::is_assignable<T &, optional<U> &>::value)
384 return false;
385 if(std::is_assignable<T &, const optional<U> &>::value)
386 return false;
387 if(std::is_assignable<T &, optional<U> &&>::value)
388 return false;
389 if(std::is_assignable<T &, const optional<U> &&>::value)
390 return false;
391 return true;
392 }
393 }
394
395 template <typename T>
396 class optional : private detail::Optional_base<T>
397 {
398 private:
399 typedef detail::Optional_base<T> Base;
400 using Base::is_full;
401 using Base::full_value;
402
403 public:
404 using Base::Base;
405 using Base::reset;
406 using Base::emplace;
407 constexpr optional() noexcept = default;
408 constexpr optional(const optional &) noexcept(std::is_nothrow_copy_constructible<T>::value) =
409 default;
410 constexpr optional(optional &&) noexcept(std::is_nothrow_move_constructible<T>::value) =
411 default;
412 template <typename U,
413 typename = typename std::
414 enable_if<detail::optional_needs_conversion_constructors<T, U, const U &>()
415 && std::is_convertible<const U &, T>::value>::type>
416 optional(const optional<U> &rt) noexcept(std::is_nothrow_constructible<T, const U &>::value)
417 : Base()
418 {
419 if(rt)
420 emplace(*rt);
421 }
422 template <typename U,
423 typename = typename std::
424 enable_if<detail::optional_needs_conversion_constructors<T, U, const U &>()
425 && !std::is_convertible<const U &, T>::value>::type,
426 typename = void>
427 explicit optional(const optional<U> &rt) noexcept(
428 std::is_nothrow_constructible<T, const U &>::value)
429 : Base()
430 {
431 if(rt)
432 emplace(*rt);
433 }
434 template <
435 typename U,
436 typename =
437 typename std::enable_if<detail::optional_needs_conversion_constructors<T, U, U &&>()
438 && std::is_convertible<U &&, T>::value>::type>
439 optional(optional<U> &&rt) noexcept(std::is_nothrow_constructible<T, U &&>::value)
440 : Base()
441 {
442 if(rt)
443 emplace(std::move(*rt));
444 }
445 template <
446 typename U,
447 typename =
448 typename std::enable_if<detail::optional_needs_conversion_constructors<T, U, U &&>()
449 && !std::is_convertible<U &&, T>::value>::type,
450 typename = void>
451 explicit optional(optional<U> &&rt) noexcept(std::is_nothrow_constructible<T, U &&>::value)
452 : Base()
453 {
454 if(rt)
455 emplace(std::move(*rt));
456 }
457 template <typename U,
458 typename = typename std::
459 enable_if<std::is_constructible<T, U &&>::value
460 && !std::is_same<typename std::decay<U>::type, in_place_t>::value
461 && !std::is_same<typename std::decay<U>::type, optional>::value
462 && std::is_convertible<U &&, T>::value>::type,
463 typename = void>
464 constexpr optional(U &&value) noexcept(std::is_nothrow_constructible<T, U &&>::value)
465 : Base(in_place, std::forward<U>(value))
466 {
467 }
468 template <typename U,
469 typename = typename std::
470 enable_if<std::is_constructible<T, U &&>::value
471 && !std::is_same<typename std::decay<U>::type, in_place_t>::value
472 && !std::is_same<typename std::decay<U>::type, optional>::value
473 && !std::is_convertible<U &&, T>::value>::type>
474 explicit constexpr optional(U &&value) noexcept(std::is_nothrow_constructible<T, U &&>::value)
475 : Base(in_place, std::forward<U>(value))
476 {
477 }
478 constexpr optional &operator=(const optional &) noexcept(
479 std::is_nothrow_copy_assignable<T>::value) = default;
480 constexpr optional &operator=(optional &&) noexcept(std::is_nothrow_move_assignable<T>::value) =
481 default;
482 template <typename U = T,
483 typename = typename std::
484 enable_if<!std::is_same<typename std::decay<U>::type, optional>::value
485 && std::is_constructible<T, U>::value
486 && std::is_assignable<T &, U>::value
487 && (!std::is_scalar<T>::value
488 || !std::is_same<typename std::decay<U>::type, T>::value)>::type>
489 optional &operator=(U &&value) noexcept(std::is_nothrow_constructible<T, U &&>::value
490 &&std::is_nothrow_assignable<T &, U &&>::value)
491 {
492 if(is_full)
493 full_value = std::forward<U>(value);
494 else
495 emplace(std::forward<U>(value));
496 return *this;
497 }
498 optional &operator=(nullopt_t) noexcept
499 {
500 reset();
501 return *this;
502 }
503 template <
504 typename U,
505 typename = typename std::enable_if< //
506 detail::optional_needs_conversion_from_optional_assign_operators<T, U, const U &>()>::
507 type>
508 optional &operator=(const optional<U> &rt) noexcept(
509 std::is_nothrow_constructible<T, const U &>::value
510 &&std::is_nothrow_assignable<T &, const U &>::value)
511 {
512 if(!rt)
513 reset();
514 else if(!is_full)
515 emplace(*rt);
516 else
517 full_value = *rt;
518 return *this;
519 }
520 template <
521 typename U,
522 typename = typename std::enable_if< //
523 detail::optional_needs_conversion_from_optional_assign_operators<T, U, U &&>()>::type>
524 optional &operator=(optional<U> &&rt) noexcept(std::is_nothrow_constructible<T, U &&>::value &&
525 std::is_nothrow_assignable<T &, U &&>::value)
526 {
527 if(!rt)
528 reset();
529 else if(!is_full)
530 emplace(std::move(*rt));
531 else
532 full_value = std::move(*rt);
533 return *this;
534 }
535 constexpr const T *operator->() const noexcept
536 {
537 assert(is_full);
538 return std::addressof(full_value);
539 }
540 constexpr T *operator->() noexcept
541 {
542 assert(is_full);
543 return std::addressof(full_value);
544 }
545 constexpr const T &operator*() const &noexcept
546 {
547 assert(is_full);
548 return full_value;
549 }
550 constexpr T &operator*() & noexcept
551 {
552 assert(is_full);
553 return full_value;
554 }
555 constexpr const T &&operator*() const &&noexcept
556 {
557 assert(is_full);
558 return std::move(full_value);
559 }
560 constexpr T &&operator*() && noexcept
561 {
562 assert(is_full);
563 return std::move(full_value);
564 }
565 constexpr explicit operator bool() const noexcept
566 {
567 return is_full;
568 }
569 constexpr bool has_value() const noexcept
570 {
571 return is_full;
572 }
573 constexpr T &value() &
574 {
575 if(!is_full)
576 throw bad_optional_access();
577 return full_value;
578 }
579 constexpr const T &value() const &
580 {
581 if(!is_full)
582 throw bad_optional_access();
583 return full_value;
584 }
585 constexpr T &&value() &&
586 {
587 if(!is_full)
588 throw bad_optional_access();
589 return std::move(full_value);
590 }
591 constexpr const T &&value() const &&
592 {
593 if(!is_full)
594 throw bad_optional_access();
595 return std::move(full_value);
596 }
597 template <typename U>
598 constexpr T value_or(U &&default_value) const &noexcept(
599 std::is_nothrow_copy_constructible<T>::value //
600 &&noexcept(static_cast<T>(std::declval<U>())))
601 {
602 return is_full ? full_value : static_cast<T>(std::forward<U>(default_value));
603 }
604 template <typename U>
605 constexpr T value_or(U &&default_value)
606 && noexcept(std::is_nothrow_copy_constructible<T>::value //
607 &&noexcept(static_cast<T>(std::declval<U>())))
608 {
609 return is_full ? std::move(full_value) : static_cast<T>(std::forward<U>(default_value));
610 }
611 void swap(optional &other) noexcept(
612 std::is_nothrow_move_constructible<T>::value &&util::is_nothrow_swappable<T>::value)
613 {
614 if(is_full)
615 {
616 if(other.is_full)
617 {
618 using std::swap;
619 swap(full_value, other.full_value);
620 }
621 else
622 {
623 other.emplace(std::move(full_value));
624 reset();
625 }
626 }
627 else if(other.is_full)
628 {
629 emplace(std::move(other.full_value));
630 other.reset();
631 }
632 }
633 };
634
635 template <typename T, typename U>
636 constexpr bool operator==(const optional<T> &l, const optional<U> &r) noexcept(noexcept(*l == *r))
637 {
638 if(!l.has_value() || !r.has_value())
639 return !r.has_value();
640 return *l == *r;
641 }
642
643 template <typename T, typename U>
644 constexpr bool operator!=(const optional<T> &l, const optional<U> &r) noexcept(noexcept(*l == *r))
645 {
646 if(!l.has_value() || !r.has_value())
647 return r.has_value();
648 return *l != *r;
649 }
650
651 template <typename T, typename U>
652 constexpr bool operator<(const optional<T> &l, const optional<U> &r) noexcept(noexcept(*l == *r))
653 {
654 if(!l.has_value() || !r.has_value())
655 return r.has_value();
656 return *l < *r;
657 }
658
659 template <typename T, typename U>
660 constexpr bool operator>(const optional<T> &l, const optional<U> &r) noexcept(noexcept(*l == *r))
661 {
662 if(!l.has_value() || !r.has_value())
663 return l.has_value();
664 return *l > *r;
665 }
666
667 template <typename T, typename U>
668 constexpr bool operator<=(const optional<T> &l, const optional<U> &r) noexcept(noexcept(*l == *r))
669 {
670 if(!l.has_value() || !r.has_value())
671 return !l.has_value();
672 return *l <= *r;
673 }
674
675 template <typename T, typename U>
676 constexpr bool operator>=(const optional<T> &l, const optional<U> &r) noexcept(noexcept(*l == *r))
677 {
678 if(!l.has_value() || !r.has_value())
679 return !r.has_value();
680 return *l >= *r;
681 }
682
683 template <typename T>
684 constexpr bool operator==(const optional<T> &v, nullopt_t) noexcept
685 {
686 return !v.has_value();
687 }
688
689 template <typename T>
690 constexpr bool operator!=(const optional<T> &v, nullopt_t) noexcept
691 {
692 return v.has_value();
693 }
694
695 template <typename T>
696 constexpr bool operator<(const optional<T> &v, nullopt_t) noexcept
697 {
698 return false;
699 }
700
701 template <typename T>
702 constexpr bool operator>(const optional<T> &v, nullopt_t) noexcept
703 {
704 return v.has_value();
705 }
706
707 template <typename T>
708 constexpr bool operator<=(const optional<T> &v, nullopt_t) noexcept
709 {
710 return !v.has_value();
711 }
712
713 template <typename T>
714 constexpr bool operator>=(const optional<T> &v, nullopt_t) noexcept
715 {
716 return true;
717 }
718
719 template <typename T>
720 constexpr bool operator==(nullopt_t, const optional<T> &v) noexcept
721 {
722 return !v.has_value();
723 }
724
725 template <typename T>
726 constexpr bool operator!=(nullopt_t, const optional<T> &v) noexcept
727 {
728 return v.has_value();
729 }
730
731 template <typename T>
732 constexpr bool operator<(nullopt_t, const optional<T> &v) noexcept
733 {
734 return v.has_value();
735 }
736
737 template <typename T>
738 constexpr bool operator>(nullopt_t, const optional<T> &v) noexcept
739 {
740 return false;
741 }
742
743 template <typename T>
744 constexpr bool operator<=(nullopt_t, const optional<T> &v) noexcept
745 {
746 return true;
747 }
748
749 template <typename T>
750 constexpr bool operator>=(nullopt_t, const optional<T> &v) noexcept
751 {
752 return !v.has_value();
753 }
754
755 template <typename T, typename U>
756 constexpr bool operator==(const optional<T> &l, const U &r) noexcept(
757 noexcept(static_cast<bool>(std::declval<const T &>() == std::declval<const U &>())))
758 {
759 if(l)
760 return *l == r;
761 return false;
762 }
763
764 template <typename T, typename U>
765 constexpr bool operator==(const U &l, const optional<T> &r) noexcept(
766 noexcept(static_cast<bool>(std::declval<const U &>() == std::declval<const T &>())))
767 {
768 if(r)
769 return l == *r;
770 return false;
771 }
772
773 template <typename T, typename U>
774 constexpr bool operator!=(const optional<T> &l, const U &r) noexcept(
775 noexcept(static_cast<bool>(std::declval<const T &>() != std::declval<const U &>())))
776 {
777 if(l)
778 return *l != r;
779 return true;
780 }
781
782 template <typename T, typename U>
783 constexpr bool operator!=(const U &l, const optional<T> &r) noexcept(
784 noexcept(static_cast<bool>(std::declval<const U &>() != std::declval<const T &>())))
785 {
786 if(r)
787 return l != *r;
788 return true;
789 }
790
791 template <typename T, typename U>
792 constexpr bool operator<(const optional<T> &l, const U &r) noexcept(
793 noexcept(static_cast<bool>(std::declval<const T &>() < std::declval<const U &>())))
794 {
795 if(l)
796 return *l < r;
797 return true;
798 }
799
800 template <typename T, typename U>
801 constexpr bool operator<(const U &l, const optional<T> &r) noexcept(
802 noexcept(static_cast<bool>(std::declval<const U &>() < std::declval<const T &>())))
803 {
804 if(r)
805 return l < *r;
806 return false;
807 }
808
809 template <typename T, typename U>
810 constexpr bool operator>(const optional<T> &l, const U &r) noexcept(
811 noexcept(static_cast<bool>(std::declval<const T &>() > std::declval<const U &>())))
812 {
813 if(l)
814 return *l > r;
815 return false;
816 }
817
818 template <typename T, typename U>
819 constexpr bool operator>(const U &l, const optional<T> &r) noexcept(
820 noexcept(static_cast<bool>(std::declval<const U &>() > std::declval<const T &>())))
821 {
822 if(r)
823 return l > *r;
824 return true;
825 }
826
827 template <typename T, typename U>
828 constexpr bool operator<=(const optional<T> &l, const U &r) noexcept(
829 noexcept(static_cast<bool>(std::declval<const T &>() <= std::declval<const U &>())))
830 {
831 if(l)
832 return *l <= r;
833 return true;
834 }
835
836 template <typename T, typename U>
837 constexpr bool operator<=(const U &l, const optional<T> &r) noexcept(
838 noexcept(static_cast<bool>(std::declval<const U &>() <= std::declval<const T &>())))
839 {
840 if(r)
841 return l <= *r;
842 return false;
843 }
844
845 template <typename T, typename U>
846 constexpr bool operator>=(const optional<T> &l, const U &r) noexcept(
847 noexcept(static_cast<bool>(std::declval<const T &>() >= std::declval<const U &>())))
848 {
849 if(l)
850 return *l >= r;
851 return false;
852 }
853
854 template <typename T, typename U>
855 constexpr bool operator>=(const U &l, const optional<T> &r) noexcept(
856 noexcept(static_cast<bool>(std::declval<const U &>() >= std::declval<const T &>())))
857 {
858 if(r)
859 return l >= *r;
860 return true;
861 }
862
863 template <typename T>
864 constexpr optional<typename std::decay<T>::type> make_optional(T &&value)
865 {
866 return optional<typename std::decay<T>::type>(in_place, std::forward<T>(value));
867 }
868
869 template <typename T, typename... Args>
870 constexpr optional<T> make_optional(Args &&... args)
871 {
872 return optional<T>(in_place, std::forward<T>(args)...);
873 }
874
875 template <typename T, typename U, typename... Args>
876 constexpr optional<T> make_optional(std::initializer_list<U> init_list, Args &&... args)
877 {
878 return optional<T>(in_place, init_list, std::forward<T>(args)...);
879 }
880
881 template <typename T,
882 typename = typename std::enable_if<std::is_move_constructible<T>::value
883 && is_swappable<T>::value>::type>
884 void swap(optional<T> &l, optional<T> &r) noexcept(noexcept(l.swap(r)))
885 {
886 l.swap(r);
887 }
888
889 namespace detail
890 {
891 template <typename T, bool Is_Enabled = std::is_default_constructible<std::hash<T>>::value>
892 struct optional_hash
893 {
894 constexpr std::size_t operator()(const optional<T> &value) const
895 noexcept(noexcept(static_cast<std::size_t>(std::hash<T>()(std::declval<const T &>()))))
896 {
897 if(value)
898 return std::hash<T>()(*value);
899 return 0;
900 }
901 };
902
903 template <typename T>
904 struct optional_hash<T, false>
905 {
906 optional_hash() noexcept = delete;
907 ~optional_hash() = delete;
908 optional_hash(const optional_hash &) noexcept = delete;
909 optional_hash &operator=(const optional_hash &) noexcept = delete;
910 std::size_t operator()(const optional<T> &value) const noexcept = delete;
911 };
912 }
913 }
914 }
915
916 namespace std
917 {
918 template <typename T>
919 struct hash<kazan::util::optional<T>> : public kazan::util::detail::optional_hash<T>
920 {
921 };
922 }
923
924 #endif /* UTIL_OPTIONAL_H_ */