optional (15571B)
1 // -*- c++ -*- 2 #pragma once 3 4 #include <stdexcept> 5 #include <utility> 6 7 namespace std 8 { 9 struct nullopt_t { explicit constexpr nullopt_t(int) {} }; 10 inline constexpr nullopt_t nullopt{0}; 11 12 namespace LibshitOptionalDetail 13 { 14 // base+destructor 15 template <typename T, bool = std::is_trivially_destructible_v<T>> 16 struct Cnt0 17 { 18 union 19 { 20 char dummy; 21 T data; 22 }; 23 bool valid = false; 24 25 constexpr Cnt0() noexcept {} 26 template <typename... Args> 27 constexpr Cnt0(std::in_place_t, Args&&... args) 28 : data(std::forward<Args>(args)...), valid(true) {} 29 30 void Reset() { data.~T(); valid = false; } 31 32 template <typename X, typename U> 33 void GenCtor(U&& o) 34 { 35 if (!o.valid) return; 36 new (this->buf) T(std::forward<X>(o.data)); 37 this->valid = true; 38 } 39 40 template <typename X, typename U> 41 void GenOpEq(U&& o) 42 { 43 if (valid && !o.valid) Reset(); 44 if (!valid && o.valid) 45 { 46 new (&data) T(std::forward<X>(o.data)); 47 this->valid = true; 48 } 49 if (valid && o.valid) data = std::forward<X>(o.data); 50 } 51 }; 52 53 template <typename T> struct Cnt0<T, false> : Cnt0<T, true> 54 { ~Cnt0() noexcept { if (this->valid) this->Reset(); } }; 55 56 57 // copy ctor 58 template <typename T, bool = std::is_copy_constructible_v<T>, 59 bool = std::is_trivially_copy_constructible_v<T>> 60 struct Cnt1; 61 62 template <typename T> struct Cnt1<T, true, true> : Cnt0<T> 63 { using Cnt0<T>::Cnt0; }; 64 65 template <typename T> struct Cnt1<T, false, false> : Cnt0<T> 66 { 67 using Cnt0<T>::Cnt0; 68 Cnt1(const Cnt1&) = delete; 69 Cnt1(Cnt1&&) = default; 70 Cnt1& operator=(const Cnt1&) = default; 71 Cnt1& operator=(Cnt1&&) = default; 72 }; 73 74 template <typename T> struct Cnt1<T, true, false> : Cnt0<T> 75 { 76 using Cnt0<T>::Cnt0; 77 Cnt1(const Cnt1& o) { this->template GenCtor<const T&>(o); } 78 Cnt1(Cnt1&&) = default; 79 Cnt1& operator=(const Cnt1&) = default; 80 Cnt1& operator=(Cnt1&&) = default; 81 }; 82 83 // move ctor 84 template <typename T, bool = std::is_move_constructible_v<T>, 85 bool = std::is_trivially_move_constructible_v<T>> 86 struct Cnt2; 87 88 template <typename T> struct Cnt2<T, true, true> : Cnt1<T> 89 { using Cnt1<T>::Cnt1; }; 90 91 template <typename T> struct Cnt2<T, false, false> : Cnt1<T> 92 { 93 using Cnt1<T>::Cnt1; 94 Cnt2(const Cnt2&) = default; 95 Cnt2(Cnt2&&) = delete; 96 Cnt2& operator=(const Cnt2&) = default; 97 Cnt2& operator=(Cnt2&&) = default; 98 }; 99 100 template <typename T> struct Cnt2<T, true, false> : Cnt1<T> 101 { 102 using Cnt1<T>::Cnt1; 103 Cnt2(const Cnt2&) = default; 104 Cnt2(Cnt2&& o) noexcept(std::is_nothrow_move_constructible_v<T>) 105 { this->template GenCtor<T>(std::move(o)); } 106 Cnt2& operator=(const Cnt2&) = default; 107 Cnt2& operator=(Cnt2&&) = default; 108 }; 109 110 // copy op= 111 template < 112 typename T, 113 bool = std::is_copy_constructible_v<T> && std::is_copy_assignable_v<T>, 114 bool = std::is_trivially_copy_constructible_v<T> && 115 std::is_trivially_copy_assignable_v<T> && 116 std::is_trivially_destructible_v<T>> 117 struct Cnt3; 118 119 template <typename T> struct Cnt3<T, true, true> : Cnt2<T> 120 { using Cnt2<T>::Cnt2; }; 121 122 template <typename T> struct Cnt3<T, false, false> : Cnt2<T> 123 { 124 using Cnt2<T>::Cnt2; 125 Cnt3(const Cnt3&) = default; 126 Cnt3(Cnt3&&) = default; 127 Cnt3& operator=(const Cnt3&) = delete; 128 Cnt3& operator=(Cnt3&&) = default; 129 }; 130 131 template <typename T> struct Cnt3<T, true, false> : Cnt2<T> 132 { 133 using Cnt2<T>::Cnt2; 134 Cnt3(const Cnt3&) = default; 135 Cnt3(Cnt3&&) = default; 136 Cnt3& operator=(const Cnt3& o) 137 { this->template GenOpEq<const T&>(o); return *this; } 138 Cnt3& operator=(Cnt3&&) = default; 139 }; 140 141 // move op= 142 template < 143 typename T, 144 bool = std::is_move_constructible_v<T> && std::is_move_assignable_v<T>, 145 bool = std::is_trivially_move_constructible_v<T> && 146 std::is_trivially_move_assignable_v<T> && 147 std::is_trivially_destructible_v<T>> 148 struct Cnt4; 149 150 template <typename T> struct Cnt4<T, true, true> : Cnt3<T> 151 { using Cnt3<T>::Cnt3; }; 152 153 template <typename T> struct Cnt4<T, false, false> : Cnt3<T> 154 { 155 using Cnt3<T>::Cnt3; 156 Cnt4(const Cnt4&) = default; 157 Cnt4(Cnt4&&) = default; 158 Cnt4& operator=(const Cnt4&) = default; 159 Cnt4& operator=(Cnt4&&) = delete; 160 }; 161 162 template <typename T> struct Cnt4<T, true, false> : Cnt3<T> 163 { 164 using Cnt3<T>::Cnt3; 165 Cnt4(const Cnt4&) = default; 166 Cnt4(Cnt4&&) = default; 167 Cnt4& operator=(const Cnt4& o) = default; 168 Cnt4& operator=(Cnt4&& o) 169 noexcept(std::is_nothrow_move_assignable_v<T> && 170 std::is_nothrow_move_constructible_v<T>) 171 { this->template GenOpEq<T>(std::move(o)); return *this; } 172 }; 173 } 174 175 176 struct bad_optional_access : std::exception 177 { 178 const char* what() const noexcept override 179 { return "bad_exception_access"; } 180 }; 181 182 template <typename T> 183 class optional : private LibshitOptionalDetail::Cnt4<T> 184 { 185 template <typename U> friend class optional; 186 187 using Base = LibshitOptionalDetail::Cnt4<T>; 188 189 template <typename U> 190 inline static constexpr const bool ConvCtor = 191 !std::is_constructible_v<T, std::optional<U>&> && 192 !std::is_constructible_v<T, const std::optional<U>&> && 193 !std::is_constructible_v<T, std::optional<U>&&> && 194 !std::is_constructible_v<T, const std::optional<U>&&> && 195 !std::is_convertible_v<std::optional<U>&, T> && 196 !std::is_convertible_v<const std::optional<U>&, T> && 197 !std::is_convertible_v<std::optional<U>&&, T> && 198 !std::is_convertible_v<const std::optional<U>&&, T>; 199 200 template <typename U> 201 inline static constexpr const bool ConvOpEq = ConvCtor<U> && 202 !std::is_assignable_v<T&, std::optional<U>&> && 203 !std::is_assignable_v<T&, const std::optional<U>&> && 204 !std::is_assignable_v<T&, std::optional<U>&&> && 205 !std::is_assignable_v<T&, const std::optional<U>&&>; 206 207 template <typename U> 208 inline static constexpr const bool ValCtor = 209 std::is_constructible_v<T, U&&> && 210 !std::is_same_v<std::in_place_t, std::decay_t<U>> && 211 !std::is_same_v<std::optional<T>, std::decay_t<U>>; 212 213 public: 214 using value_type = T; 215 216 constexpr optional() noexcept {} 217 constexpr optional(nullopt_t) noexcept {} 218 constexpr optional(const optional&) = default; 219 constexpr optional(optional&&) = default; 220 221 template <typename U, std::enable_if_t< 222 std::is_constructible_v<T, const U&> && ConvCtor<U> && 223 std::is_convertible_v<const U&, T>>* = nullptr> 224 optional(const optional<U>& o) 225 { this->template GenCtor<const U&>(o); } 226 227 template <typename U, std::enable_if_t< 228 std::is_constructible_v<T, const U&> && ConvCtor<U> && 229 !std::is_convertible_v<const U&, T>>* = nullptr> 230 explicit optional(const optional<U>& o) 231 { this->template GenCtor<const U&>(o); } 232 233 template <typename U, std::enable_if_t< 234 std::is_constructible_v<T, U&&> && ConvCtor<U> && 235 std::is_convertible_v<U&&, T>>* = nullptr> 236 optional(optional<U>&& o) 237 { this->template GenCtor<U>(std::move(o)); } 238 239 template <typename U, std::enable_if_t< 240 std::is_constructible_v<T, U&&> && ConvCtor<U> && 241 !std::is_convertible_v<U&&, T>>* = nullptr> 242 explicit optional(optional<U>&& o) 243 { this->template GenCtor<U>(std::move(o)); } 244 245 template <typename... Args, 246 std::enable_if_t<std::is_constructible_v<T, Args...>>* = nullptr> 247 constexpr explicit optional(std::in_place_t, Args&&... args) 248 : Base(in_place, std::forward<Args>(args)...) {} 249 250 template <typename U, typename... Args, std::enable_if_t< 251 std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>>* = nullptr> 252 constexpr explicit optional( 253 std::in_place_t, std::initializer_list<U> list, Args&&... args) 254 : Base(in_place, list, std::forward<Args>(args)...) {} 255 256 template <typename U = value_type, std::enable_if_t< 257 ValCtor<U> && std::is_convertible_v<U&&, T>>* = nullptr> 258 constexpr optional(U&& value) 259 : Base(in_place, std::forward<U>(value)) {} 260 261 template <typename U = value_type, std::enable_if_t< 262 ValCtor<U> && !std::is_convertible_v<U&&, T>>* = nullptr> 263 explicit constexpr optional(U&& value) 264 : Base(in_place, std::forward<U>(value)) {} 265 266 // --- 267 268 optional& operator=(const optional&) = default; 269 optional& operator=(optional&&) = default; 270 271 optional& operator=(std::nullopt_t) noexcept 272 { reset(); return *this; } 273 274 template <typename U = T, std::enable_if_t< 275 !std::is_same_v<std::decay_t<U>, std::optional<T>> && 276 std::is_constructible_v<T, U> && std::is_assignable_v<T&, U> && ( 277 !std::is_scalar_v<T> || !std::is_same_v<std::decay_t<U>, T>)>* = nullptr> 278 optional& operator=(U&& value) 279 { 280 if (this->valid) this->data = std::forward<U>(value); 281 else { new (&this->data) T(std::forward<U>(value)); this->valid = true; } 282 return *this; 283 } 284 285 template <typename U, std::enable_if_t< 286 ConvOpEq<U> && std::is_constructible_v<T, const U&> && 287 std::is_assignable_v<T&, const U&>>* = nullptr> 288 optional& operator=(const optional<U>& o) 289 { this->template GenOpEq<const U&>(o); return *this; } 290 291 template <typename U, std::enable_if_t< 292 ConvOpEq<U> && std::is_constructible_v<T, U> && 293 std::is_assignable_v<T&, U>>* = nullptr> 294 optional& operator=(optional<U>&& o) 295 { this->template GenOpEq<U>(std::move(o)); return *this; } 296 297 // --- 298 299 constexpr T* operator->() { return &this->data; } 300 constexpr const T* operator->() const { return &this->data; } 301 302 constexpr T& operator*() & { return this->data; } 303 constexpr const T& operator*() const & { return this->data; } 304 constexpr T&& operator*() && { return std::move(this->data); } 305 constexpr const T&& operator*() const && { return std::move(this->data); } 306 307 constexpr explicit operator bool() const noexcept { return this->valid; } 308 constexpr bool has_value() const noexcept { return this->valid; } 309 310 constexpr T& value() & 311 { if (this->valid) return this->data; else throw bad_optional_access{}; } 312 constexpr const T& value() const & 313 { if (this->valid) return this->data; else throw bad_optional_access{}; } 314 315 constexpr T&& value() && 316 { 317 if (this->valid) return std::move(this->data); 318 else throw bad_optional_access{}; 319 } 320 constexpr const T&& value() const && 321 { 322 if (this->valid) return std::move(this->data); 323 else throw bad_optional_access{}; 324 } 325 326 template <typename U> constexpr T value_or(U&& def) const& 327 { 328 if (this->valid) return this->data; 329 else return static_cast<T>(std::forward<U>(def)); 330 } 331 332 template <typename U> constexpr T value_or(U&& def) && 333 { 334 if (this->valid) return std::move(this->data); 335 else return static_cast<T>(std::forward<U>(def)); 336 } 337 338 // --- 339 340 void swap(optional& o) noexcept(std::is_nothrow_move_constructible_v<T> && 341 std::is_nothrow_swappable_v<T>) 342 { 343 if (this->valid && !o.valid) 344 { 345 o.template GenCtor<T>(std::move(this->data)); 346 this->Reset(); 347 } 348 if (!this->valid && o.valid) 349 { 350 this->template GenCtor<T>(std::move(o.data)); 351 o.Reset(); 352 } 353 if (this->valid && o.valid) 354 { 355 using std::swap; 356 swap(this->data, o.data); 357 } 358 } 359 360 void reset() noexcept 361 { 362 // both gcc and clang can't optimize if (value) Reset() into value = false 363 // with trivially destructible types, so be a bit more explicit 364 if constexpr (std::is_trivially_destructible_v<T>) this->valid = false; 365 else if (this->valid) this->Reset(); 366 } 367 368 // --- 369 370 template <typename... Args> 371 T& emplace(Args&&... args) 372 { 373 reset(); 374 new (&this->data) T(std::forward<Args>(args)...); 375 this->valid = true; 376 return this->data; 377 } 378 379 template <typename U, typename... Args, std::enable_if_t< 380 std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>>* = nullptr> 381 T& emplace(std::initializer_list<U> list, Args&&... args) 382 { 383 reset(); 384 new (&this->data) T(list, std::forward<Args>(args)...); 385 this->valid = true; 386 return this->data; 387 } 388 389 }; 390 391 template <typename T> optional(T) -> optional<T>; 392 393 // --- 394 395 template <typename T> 396 constexpr optional<std::decay_t<T>> make_optional(T&& value) 397 { return optional<std::decay_t<T>>(std::forward<T>(value)); } 398 399 template <typename T, typename... Args> 400 constexpr optional<T> make_optional(Args&&... args) 401 { return optional<T>(std::in_place, std::forward<Args>(args)...); } 402 403 template <typename T, typename U, typename... Args> 404 constexpr optional<T> make_optional(std::initializer_list<U> list, Args&&... args) 405 { return optional<T>(std::in_place, list, std::forward<Args>(args)...); } 406 407 // --- 408 409 template <typename T, std::enable_if_t< 410 std::is_move_constructible_v<T> && std::is_swappable_v<T>>* = nullptr> 411 void swap(optional<T>& lhs, optional<T>& rhs) 412 noexcept(noexcept(lhs.swap(rhs))) 413 { lhs.swap(rhs); } 414 415 // todo: enabled/disabled not supported 416 template <typename T> 417 struct hash<optional<T>> 418 { 419 size_t operator()(const optional<T>& o) const 420 { return o ? hash<std::remove_const_t<T>>()(*o) : 0xb19b00b2; } 421 }; 422 423 // --- 424 425 #define LIBSHIT_GEN(op) \ 426 template <typename T> \ 427 constexpr bool operator op(const optional<T>& a, const optional<T>& b) \ 428 { \ 429 if (!a || !b) return bool(a) op bool(b); \ 430 return *a op *b; \ 431 } \ 432 template <typename T> \ 433 constexpr bool operator op(const optional<T>& o, nullopt_t) \ 434 { return bool(o) op false; } \ 435 template <typename T> \ 436 constexpr bool operator op(nullopt_t, const optional<T>& o) \ 437 { return false op bool(o); } \ 438 template <typename T, typename U> \ 439 constexpr bool operator op(const optional<T>& a, const U& b) \ 440 { \ 441 if (!a) return bool(a) op true; \ 442 return *a op b; \ 443 } \ 444 template <typename T, typename U> \ 445 constexpr bool operator op(const T& a, const optional<U>& b) \ 446 { \ 447 if (!b) return true op bool(b); \ 448 return a op *b; \ 449 } 450 LIBSHIT_GEN(==) LIBSHIT_GEN(!=) LIBSHIT_GEN(<) LIBSHIT_GEN(<=) 451 LIBSHIT_GEN(>) LIBSHIT_GEN(>=) 452 #undef LIBSHIT_GEN 453 }