-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathoptional.hpp
169 lines (142 loc) · 6.58 KB
/
optional.hpp
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
#ifndef EXTALLOC_OPTIONAL_HPP
#define EXTALLOC_OPTIONAL_HPP
#include <cassert>
#include <new>
#include <stdexcept>
#include <type_traits>
namespace extalloc {
namespace details {
template <typename T>
struct remove_cvref {
using type = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
};
template <typename T>
using remove_cvref_t = typename remove_cvref<T>::type;
} // end namespace details
template <typename T, typename = typename std::enable_if<!std::is_reference<T>::value>::type>
class optional {
public:
using value_type = T;
static_assert (std::is_object<value_type>::value,
"Instantiation of optional<> with a non-object type is undefined behavior.");
static_assert (std::is_nothrow_destructible<value_type>::value,
"Instantiation of optional<> with an object type that is not noexcept "
"destructible is undefined behavior.");
/// Constructs an object that does not contain a value.
constexpr optional () noexcept = default;
/// Constructs an optional object that contains a value, initialized with the expression
/// std::forward<U>(value).
template <typename U,
typename = typename std::enable_if<
std::is_constructible<T, U &&>::value &&
!std::is_same<typename details::remove_cvref_t<U>, optional<T>>::value>::type>
explicit optional (U && value) noexcept (std::is_nothrow_move_constructible<T>::value &&
std::is_nothrow_copy_constructible<T>::value &&
!std::is_convertible<U &&, T>::value) {
new (&storage_) T (std::forward<U> (value));
valid_ = true;
}
/// Copy constructor: If other contains a value, initializes the contained value with the
/// expression *other. If other does not contain a value, constructs an object that does not
/// contain a value.
optional (optional const & other) noexcept (std::is_nothrow_copy_constructible<T>::value) {
if (other.valid_) {
new (&storage_) T (*other);
valid_ = true;
}
}
/// Move constructor: If other contains a value, initializes the contained value with the
/// expression std::move(*other) and does not make other empty: a moved-from optional still
/// contains a value, but the value itself is moved from. If other does not contain a value,
/// constructs an object that does not contain a value.
optional (optional && other) noexcept (std::is_nothrow_move_constructible<T>::value) {
if (other.valid_) {
new (&storage_) T (std::move (*other));
valid_ = true;
}
}
~optional () noexcept { this->reset (); }
/// If *this contains a value, destroy it. *this does not contain a value after this call.
void reset () noexcept {
if (valid_) {
// Set valid_ to false before calling the dtor.
valid_ = false;
reinterpret_cast<T const *> (&storage_)->~T ();
}
}
optional & operator= (optional const & other) noexcept (
std::is_nothrow_copy_assignable<T>::value &&
std::is_nothrow_copy_constructible<T>::value) {
if (&other != this) {
if (!other.has_value ()) {
this->reset ();
} else {
this->operator= (other.value ());
}
}
return *this;
}
optional &
operator= (optional && other) noexcept (std::is_nothrow_move_assignable<T>::value &&
std::is_nothrow_move_constructible<T>::value) {
if (&other != this) {
if (!other.has_value ()) {
this->reset ();
} else {
this->operator= (std::forward<T> (other.value ()));
}
}
return *this;
}
template <typename U,
typename = typename std::enable_if<
std::is_constructible<T, U &&>::value &&
!std::is_same<typename details::remove_cvref_t<U>, optional<T>>::value>::type>
optional & operator= (U && other) noexcept (
std::is_nothrow_copy_assignable<T>::value && std::is_nothrow_copy_constructible<
T>::value && std::is_nothrow_move_assignable<T>::value &&
std::is_nothrow_move_constructible<T>::value) {
if (valid_) {
T temp = std::forward<U> (other);
std::swap (this->value (), temp);
} else {
new (&storage_) T (std::forward<U> (other));
valid_ = true;
}
return *this;
}
bool operator== (optional const & other) const {
return this->has_value () == other.has_value () &&
(!this->has_value () || this->value () == other.value ());
}
bool operator!= (optional const & other) const { return !operator== (other); }
/// accesses the contained value
T const & operator* () const noexcept { return *(operator-> ()); }
/// accesses the contained value
T & operator* () noexcept { return *(operator-> ()); }
/// accesses the contained value
T const * operator-> () const noexcept { return &value (); }
/// accesses the contained value
T * operator-> () noexcept { return &value (); }
/// checks whether the object contains a value
constexpr explicit operator bool () const noexcept { return valid_; }
/// checks whether the object contains a value
constexpr bool has_value () const noexcept { return valid_; }
T const & value () const noexcept {
assert (valid_);
return reinterpret_cast<T const &> (storage_);
}
T & value () noexcept {
assert (valid_);
return reinterpret_cast<T &> (storage_);
}
template <typename U>
constexpr T value_or (U && default_value) const {
return this->has_value () ? this->value () : default_value;
}
private:
bool valid_ = false;
typename std::aligned_storage<sizeof (T), alignof (T)>::type storage_;
};
} // end namespace extalloc
#endif // EXTALLOC_OPTIONAL_HPP