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 */