iceberg-cpp
Loading...
Searching...
No Matches
expected.h
Go to the documentation of this file.
1/*
2 * MIT License
3 *
4 * Copyright (c) 2024 zeus-cpp
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
69
70#pragma once
71
72#include <exception>
73#include <functional>
74#include <type_traits>
75#include <utility>
76#include <version>
77
78#include "iceberg/compat/compiler.h"
80#if !ICEBERG_CXX20_COMPAT && defined(__cpp_lib_expected) && __cpp_lib_expected >= 202202L
81# include <expected>
82#endif
83
84// NOLINTBEGIN
85
86#if defined(_MSVC_LANG)
87# define ICEBERG_EXPECTED_CPLUSPLUS _MSVC_LANG
88#else
89# define ICEBERG_EXPECTED_CPLUSPLUS __cplusplus
90#endif
91
92#if ICEBERG_EXPECTED_CPLUSPLUS < 201703L
93static_assert(false, "This expected variant requires C++17");
94#endif
95
96#if ICEBERG_EXPECTED_CPLUSPLUS >= 202002L
97# define ICEBERG_EXPECTED_CONSTEXPR_DTOR constexpr
98#else
99# define ICEBERG_EXPECTED_CONSTEXPR_DTOR
100#endif
101
102// Detect exception support
103#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
104# define ICEBERG_EXPECTED_THROW(e) (throw(e))
105#else
106# define ICEBERG_EXPECTED_THROW(e) ((void)(e), std::terminate())
107#endif
108
109namespace iceberg {
110
111namespace expected_detail {
112
113template <class T, class... Args>
114constexpr T* construct_at(T* p, Args&&... args) noexcept(
115 noexcept(::new (static_cast<void*>(p)) T(std::forward<Args>(args)...))) {
116#if ICEBERG_EXPECTED_CPLUSPLUS >= 202002L
117 return std::construct_at(p, std::forward<Args>(args)...);
118#else
119 return ::new (static_cast<void*>(p)) T(std::forward<Args>(args)...);
120#endif
121}
122
123template <class T>
125 using type = std::remove_cv_t<std::remove_reference_t<T>>;
126};
127template <class T>
128using remove_cvref_t = typename remove_cvref<T>::type;
129
130template <class T, template <class...> class Template>
131inline constexpr bool is_specialization_v =
132 false; // true if and only if T is a specialization of Template
133template <template <class...> class Template, class... Types>
134inline constexpr bool is_specialization_v<Template<Types...>, Template> = true;
135
136template <class T, class U>
137using is_void_or = std::conditional_t<std::is_void_v<T>, std::true_type, U>;
138template <class T, class U>
139inline constexpr bool is_void_or_v = is_void_or<T, U>::value;
140
141template <class T>
142inline constexpr bool is_trivially_destructible_or_void_v =
143 is_void_or_v<T, std::is_trivially_destructible<T>>;
144
145template <class T>
146inline constexpr bool is_trivially_copy_constructible_or_void_v =
147 is_void_or_v<T, std::is_trivially_copy_constructible<T>>;
148
149template <class T>
150inline constexpr bool is_trivially_move_constructible_or_void_v =
151 is_void_or_v<T, std::is_trivially_move_constructible<T>>;
152
153template <class T>
154inline constexpr bool is_trivially_copy_assignable_or_void_v =
155 is_void_or_v<T, std::is_trivially_copy_assignable<T>>;
156
157template <class T>
158inline constexpr bool is_trivially_move_assignable_or_void_v =
159 is_void_or_v<T, std::is_trivially_move_assignable<T>>;
160
161template <class T>
162inline constexpr bool is_nothrow_copy_constructible_or_void_v =
163 is_void_or_v<T, std::is_nothrow_copy_constructible<T>>;
164
165template <class T>
166inline constexpr bool is_nothrow_move_constructible_or_void_v =
167 is_void_or_v<T, std::is_nothrow_move_constructible<T>>;
168
169template <class T>
170inline constexpr bool is_default_constructible_or_void_v =
171 is_void_or_v<T, std::is_default_constructible<T>>;
172
173template <class T>
174inline constexpr bool is_copy_constructible_or_void_v =
175 is_void_or_v<T, std::is_copy_constructible<T>>;
176
177template <class T>
178inline constexpr bool is_move_constructible_or_void_v =
179 is_void_or_v<T, std::is_move_constructible<T>>;
180
181template <class T>
182inline constexpr bool is_copy_assignable_or_void_v =
183 is_void_or_v<T, std::is_copy_assignable<T>>;
184
185template <class T>
186inline constexpr bool is_move_assignable_or_void_v =
187 is_void_or_v<T, std::is_move_assignable<T>>;
188
189template <class From, class To>
190inline constexpr bool is_nothrow_convertible_v =
191 noexcept(static_cast<To>(std::declval<From>()));
192
193template <class Lhs, class Rhs, class = void>
194struct is_equality_result_convertible_to_bool : std::false_type {};
195
196template <class Lhs, class Rhs>
198 Lhs, Rhs,
199 std::void_t<decltype(std::declval<const Lhs&>() == std::declval<const Rhs&>())>>
200 : std::is_convertible<
201 decltype(std::declval<const Lhs&>() == std::declval<const Rhs&>()), bool> {};
202
203template <class Lhs, class Rhs>
204inline constexpr bool is_equality_result_convertible_to_bool_v =
206
207constexpr bool implicitly_convert_to_bool(bool value) noexcept { return value; }
208
209template <class Lhs, class Rhs, class = void>
211
212template <class Lhs, class Rhs>
214 Lhs, Rhs,
215 std::void_t<decltype(implicitly_convert_to_bool(std::declval<const Lhs&>() ==
216 std::declval<const Rhs&>()))>>
217 : std::bool_constant<noexcept(implicitly_convert_to_bool(
218 std::declval<const Lhs&>() == std::declval<const Rhs&>()))> {};
219
220template <class Lhs, class Rhs>
221inline constexpr bool is_nothrow_equality_result_convertible_to_bool_v =
223
224} // namespace expected_detail
225
226template <class E>
227class unexpected;
228
229namespace expected_detail {
230
231template <class E>
232struct is_error_type_valid : std::true_type {
233 static_assert(std::is_object_v<E>, "E must be an object type");
234 static_assert(!std::is_array_v<E>, "E must not be an array");
235 static_assert(!std::is_const_v<E>, "E must not be const");
236 static_assert(!std::is_volatile_v<E>, "E must not be volatile");
237 static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<E>, unexpected>,
238 "E must not be unexpected");
239};
240template <class T>
241inline constexpr bool is_error_type_valid_v = is_error_type_valid<T>::value;
242
243} // namespace expected_detail
244
245template <class E>
246class ICEBERG_EXPORT [[nodiscard]] unexpected {
247 public:
248 static_assert(expected_detail::is_error_type_valid_v<E>);
249
250 unexpected() = delete;
251
252 ~unexpected() = default;
253
254 unexpected(const unexpected&) = default;
255
256 unexpected(unexpected&&) = default;
257
258 template <class... Args,
259 typename std::enable_if_t<std::is_constructible_v<E, Args...>>* = nullptr>
260 constexpr explicit unexpected(std::in_place_t, Args&&... args) noexcept(
261 std::is_nothrow_constructible_v<E, Args...>)
262 : m_val(std::forward<Args>(args)...) {}
263
264 template <class U, class... Args,
265 typename std::enable_if_t<std::is_constructible_v<
266 E, std::initializer_list<U>&, Args...>>* = nullptr>
267 constexpr explicit unexpected(
268 std::in_place_t, std::initializer_list<U> l,
269 Args&&... args) noexcept(std::is_nothrow_constructible_v<E,
270 std::initializer_list<U>&,
271 Args...>)
272 : m_val(l, std::forward<Args>(args)...) {}
273
274 template <class Err = E,
275 typename std::enable_if_t<std::is_constructible_v<E, Err>>* = nullptr>
276 constexpr explicit unexpected(Err&& e) noexcept(std::is_nothrow_constructible_v<E, Err>)
277 : m_val(std::forward<Err>(e)) {}
278
279 unexpected& operator=(const unexpected&) = default;
280 unexpected& operator=(unexpected&&) = default;
281
282 constexpr const E& error() const& noexcept { return m_val; }
283 constexpr E& error() & noexcept { return m_val; }
284 constexpr E&& error() && noexcept { return std::move(m_val); }
285 constexpr const E&& error() const&& noexcept { return std::move(m_val); }
286
287 constexpr void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v<E>) {
288 static_assert(std::is_swappable_v<E>, "E must be swappable");
289 using std::swap;
290 swap(m_val, other.m_val);
291 }
292
293 friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) {
294 x.swap(y);
295 }
296
297 template <class E2>
298 friend constexpr bool operator==(
299 const unexpected& lhs,
300 const unexpected<E2>& rhs) noexcept(noexcept(lhs.error() == rhs.error())) {
301 return lhs.error() == rhs.error();
302 }
303 template <class E2>
304 friend constexpr bool operator!=(
305 const unexpected& lhs,
306 const unexpected<E2>& rhs) noexcept(noexcept(lhs.error() != rhs.error())) {
307 return lhs.error() != rhs.error();
308 }
309
310 private:
311 E m_val;
312};
313
314// deduction guide
315template <class E>
317
318struct ICEBERG_EXPORT unexpect_t {
319 explicit unexpect_t() = default;
320};
321inline constexpr unexpect_t unexpect{};
322
323template <class E>
324class bad_expected_access;
325
326template <>
327class ICEBERG_EXPORT bad_expected_access<void> : public std::exception {
328 public:
329 [[nodiscard]] const char* what() const noexcept override {
330 return "Bad expected access";
331 }
332
333 protected:
334 // LWG-4031
335 bad_expected_access() noexcept = default;
336 bad_expected_access(const bad_expected_access&) noexcept = default;
337 bad_expected_access(bad_expected_access&&) noexcept = default;
338 bad_expected_access& operator=(const bad_expected_access&) noexcept = default;
339 bad_expected_access& operator=(bad_expected_access&&) noexcept = default;
340 ~bad_expected_access() override = default;
341};
342
343template <class E>
344class ICEBERG_EXPORT bad_expected_access : public bad_expected_access<void> {
345 public:
346 explicit bad_expected_access(E e) noexcept(std::is_nothrow_move_constructible_v<E>)
347 : m_val(std::move(e)) {}
348
349 const E& error() const& noexcept { return m_val; }
350 E& error() & noexcept { return m_val; }
351 const E&& error() const&& noexcept { return std::move(m_val); }
352 E&& error() && noexcept { return std::move(m_val); }
353
354 private:
355 E m_val;
356};
357
358template <class T, class E>
359class expected;
360
361namespace expected_detail {
362
363template <class T>
364struct is_value_type_valid : std::true_type {
365 static_assert(!std::is_reference_v<T>, "T must not be a reference");
366 static_assert(!std::is_function_v<T>, "T must not be a function");
367 static_assert(!std::is_array_v<T>,
368 "T must not be an array to meet the requirements of Cpp17Destructible "
369 "when T is not cv-void");
370 static_assert(!std::is_same_v<T, std::remove_cv_t<std::in_place_t>>,
371 "T must not be in_place_t");
372 static_assert(!std::is_same_v<T, std::remove_cv_t<unexpect_t>>,
373 "T must not be unexpect_t");
374 static_assert(!expected_detail::is_specialization_v<std::remove_cv_t<T>, unexpected>,
375 "T must not be unexpected");
376};
377template <class T>
378inline constexpr bool is_value_type_valid_v = is_value_type_valid<T>::value;
379
380template <class T, class E, class U>
381using enable_forward_t = std::enable_if_t<
382 std::is_constructible_v<T, U> &&
383 !std::is_same_v<expected_detail::remove_cvref_t<U>, std::in_place_t> &&
384 !std::is_same_v<expected<T, E>, expected_detail::remove_cvref_t<U>> &&
385 !expected_detail::is_specialization_v<expected_detail::remove_cvref_t<U>,
386 unexpected> &&
387 (!std::is_same_v<std::remove_cv_t<T>, bool> || // LWG-3836
388 !expected_detail::is_specialization_v<expected_detail::remove_cvref_t<U>,
389 expected>) &&
390 !std::is_same_v<expected_detail::remove_cvref_t<U>, unexpect_t> // LWG-4222
391 >;
392
393template <class T, class E, class U, class G, class UF, class GF>
394using enable_from_other_expected_t = std::enable_if_t<
395 std::is_constructible_v<T, UF> && std::is_constructible_v<E, GF> &&
396 std::disjunction_v<std::is_same<std::remove_cv_t<T>, bool>, // LWG-3836
397 std::negation<std::disjunction<
398 std::is_constructible<T, expected<U, G>&>,
399 std::is_constructible<T, expected<U, G>>,
400 std::is_constructible<T, const expected<U, G>&>,
401 std::is_constructible<T, const expected<U, G>>,
402 std::is_convertible<expected<U, G>&, T>,
403 std::is_convertible<expected<U, G>&&, T>,
404 std::is_convertible<const expected<U, G>&, T>,
405 std::is_convertible<const expected<U, G>&&, T>,
406 std::is_constructible<unexpected<E>, expected<U, G>&>,
407 std::is_constructible<unexpected<E>, expected<U, G>>,
408 std::is_constructible<unexpected<E>, const expected<U, G>&>,
409 std::is_constructible<unexpected<E>, const expected<U, G>>>>>>;
410
411template <class E, class U, class G, class GF>
412using enable_from_other_void_expected_t =
413 std::enable_if_t<std::is_void_v<U> && std::is_constructible_v<E, GF> &&
414 std::negation_v<std::disjunction<
415 std::is_constructible<unexpected<E>, expected<U, G>&>,
416 std::is_constructible<unexpected<E>, expected<U, G>>,
417 std::is_constructible<unexpected<E>, const expected<U, G>&>,
418 std::is_constructible<unexpected<E>, const expected<U, G>>>>>;
419
420} // namespace expected_detail
421
422namespace expected_detail {
423
424// Constructing the active alternative inside storage_base ensures its
425// destructor only ever observes a fully initialized value or error.
427 explicit construct_from_expected_t() = default;
428};
429inline constexpr construct_from_expected_t construct_from_expected{};
430
431template <class Rhs>
432constexpr bool expected_source_has_value(const Rhs& rhs) noexcept {
433 if constexpr (expected_detail::is_specialization_v<expected_detail::remove_cvref_t<Rhs>,
434 expected>) {
435 return rhs.has_value();
436 } else {
437 return rhs.m_has_val;
438 }
439}
440
441template <class Rhs>
442constexpr decltype(auto) expected_source_value(Rhs&& rhs) noexcept {
443 if constexpr (expected_detail::is_specialization_v<expected_detail::remove_cvref_t<Rhs>,
444 expected>) {
445 return *std::forward<Rhs>(rhs);
446 } else {
447 if constexpr (std::is_lvalue_reference_v<Rhs>) {
448 return (rhs.m_val);
449 } else {
450 return std::move(rhs.m_val);
451 }
452 }
453}
454
455template <class Rhs>
456constexpr decltype(auto) expected_source_error(Rhs&& rhs) noexcept {
457 if constexpr (expected_detail::is_specialization_v<expected_detail::remove_cvref_t<Rhs>,
458 expected>) {
459 return std::forward<Rhs>(rhs).error();
460 } else {
461 if constexpr (std::is_lvalue_reference_v<Rhs>) {
462 return (rhs.m_unexpect);
463 } else {
464 return std::move(rhs.m_unexpect);
465 }
466 }
467}
468
470 explicit construct_with_invoke_result_t() = default;
471};
472inline constexpr construct_with_invoke_result_t construct_with_invoke_result{};
473
474template <class T>
475struct [[nodiscard]] ReinitGuard {
476 static_assert(std::is_nothrow_move_constructible_v<T>);
477
478 ReinitGuard(ReinitGuard const&) = delete;
479 ReinitGuard& operator=(ReinitGuard const&) = delete;
480
481 constexpr ReinitGuard(T* target, T* tmp) noexcept : _target(target), _tmp(tmp) {}
482 ICEBERG_EXPECTED_CONSTEXPR_DTOR ~ReinitGuard() noexcept {
483 if (_target) {
484 expected_detail::construct_at(_target, std::move(*_tmp));
485 }
486 }
487 T* _target;
488 T* _tmp;
489};
490
491template <class First, class Second, class... Args>
492constexpr void reinit_expected(First& new_val, Second& old_val, Args&&... args) noexcept(
493 std::is_nothrow_constructible_v<First, Args...>) {
494 if constexpr (std::is_nothrow_constructible_v<First, Args...>) {
495 if constexpr (!std::is_trivially_destructible_v<Second>) {
496 old_val.~Second();
497 }
498 expected_detail::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
499 } else if constexpr (std::is_nothrow_move_constructible_v<First>) {
500 First tmp(std::forward<Args>(args)...);
501 if constexpr (!std::is_trivially_destructible_v<Second>) {
502 old_val.~Second();
503 }
504 expected_detail::construct_at(std::addressof(new_val), std::move(tmp));
505 } else {
506 Second tmp(std::move(old_val));
507 if constexpr (!std::is_trivially_destructible_v<Second>) {
508 old_val.~Second();
509 }
510
511 expected_detail::ReinitGuard<Second> guard{std::addressof(old_val),
512 std::addressof(tmp)};
513 expected_detail::construct_at(std::addressof(new_val), std::forward<Args>(args)...);
514 guard._target = nullptr;
515 }
516}
517
518// Implements the storage of the values, and ensures that the destructor is
519// trivial if it can be.
520//
521// This specialization is for when `T` and `E` are trivially destructible
522template <class T, class E,
523 bool = is_trivially_destructible_or_void_v<T> &&
524 std::is_trivially_destructible_v<E>>
526 storage_base(storage_base const&) = default;
527 storage_base(storage_base&&) = default;
528 storage_base& operator=(storage_base const&) = default;
529 storage_base& operator=(storage_base&&) = default;
530
531 // iceberg: constrained so MSVC never instantiates T{} for a T without an
532 // accessible default constructor.
533 template <class U = T, std::enable_if_t<std::is_default_constructible_v<U>, int> = 0>
534 constexpr storage_base() noexcept(std::is_nothrow_default_constructible_v<U>)
535 : m_val(), m_has_val(true) {}
536
537 template <class Rhs>
538 constexpr storage_base(construct_from_expected_t, Rhs&& rhs) noexcept(
539 std::is_nothrow_constructible_v<
540 T, decltype(expected_detail::expected_source_value(std::declval<Rhs>()))> &&
541 std::is_nothrow_constructible_v<
542 E, decltype(expected_detail::expected_source_error(std::declval<Rhs>()))>)
543 : m_dummy(), m_has_val(expected_detail::expected_source_has_value(rhs)) {
544 if (m_has_val) {
545 expected_detail::construct_at(
546 std::addressof(m_val),
547 expected_detail::expected_source_value(std::forward<Rhs>(rhs)));
548 } else {
549 expected_detail::construct_at(
550 std::addressof(m_unexpect),
551 expected_detail::expected_source_error(std::forward<Rhs>(rhs)));
552 }
553 }
554
555 template <class... Args,
556 std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
557 constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
558 noexcept(T(std::forward<Args>(args)...)))
559 : m_val(std::forward<Args>(args)...), m_has_val(true) {}
560
561 template <class U, class... Args,
562 std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
563 Args&&...>>* = nullptr>
564 constexpr explicit storage_base(
565 std::in_place_t, std::initializer_list<U> il,
566 Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
567 : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
568
569 template <class... Args,
570 std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
571 constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
572 noexcept(E(std::forward<Args>(args)...)))
573 : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
574
575 template <class U, class... Args,
576 std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
577 Args&&...>>* = nullptr>
578 constexpr explicit storage_base(
579 unexpect_t, std::initializer_list<U> il,
580 Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
581 : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
582
583 // helper ctor for transform()
584 template <class Fn, class... Args>
585 constexpr explicit storage_base(
587 Args&&... args) noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
588 std::forward<Args>(
589 args)...))))
590 : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
591 m_has_val(true) {}
592
593 // helper ctor for transform_error()
594 template <class Fn, class... Args>
595 constexpr explicit storage_base(
597 Args&&... args) noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
598 std::forward<Args>(
599 args)...))))
600 : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
601 m_has_val(false) {}
602
603 ~storage_base() = default;
604
605 union {
606 T m_val;
607 E m_unexpect;
608 char m_dummy;
609 };
610 bool m_has_val;
611};
612
613template <class T, class E>
614struct storage_base<T, E, false> {
615 storage_base(storage_base const&) = default;
616 storage_base(storage_base&&) = default;
617 storage_base& operator=(storage_base const&) = default;
618 storage_base& operator=(storage_base&&) = default;
619
620 // iceberg: constrained so MSVC never instantiates T{} for a T without an
621 // accessible default constructor.
622 template <class U = T, std::enable_if_t<std::is_default_constructible_v<U>, int> = 0>
623 constexpr storage_base() noexcept(std::is_nothrow_default_constructible_v<U>)
624 : m_val(), m_has_val(true) {}
625
626 template <class Rhs>
627 constexpr storage_base(construct_from_expected_t, Rhs&& rhs) noexcept(
628 std::is_nothrow_constructible_v<
629 T, decltype(expected_detail::expected_source_value(std::declval<Rhs>()))> &&
630 std::is_nothrow_constructible_v<
631 E, decltype(expected_detail::expected_source_error(std::declval<Rhs>()))>)
632 : m_dummy(), m_has_val(expected_detail::expected_source_has_value(rhs)) {
633 if (m_has_val) {
634 expected_detail::construct_at(
635 std::addressof(m_val),
636 expected_detail::expected_source_value(std::forward<Rhs>(rhs)));
637 } else {
638 expected_detail::construct_at(
639 std::addressof(m_unexpect),
640 expected_detail::expected_source_error(std::forward<Rhs>(rhs)));
641 }
642 }
643
644 template <class... Args,
645 std::enable_if_t<std::is_constructible_v<T, Args&&...>>* = nullptr>
646 constexpr explicit storage_base(std::in_place_t, Args&&... args) noexcept(
647 noexcept(T(std::forward<Args>(args)...)))
648 : m_val(std::forward<Args>(args)...), m_has_val(true) {}
649
650 template <class U, class... Args,
651 std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
652 Args&&...>>* = nullptr>
653 constexpr explicit storage_base(
654 std::in_place_t, std::initializer_list<U> il,
655 Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
656 : m_val(il, std::forward<Args>(args)...), m_has_val(true) {}
657
658 template <class... Args,
659 std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
660 constexpr explicit storage_base(unexpect_t, Args&&... args) noexcept(
661 noexcept(E(std::forward<Args>(args)...)))
662 : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
663
664 template <class U, class... Args,
665 std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
666 Args&&...>>* = nullptr>
667 constexpr explicit storage_base(
668 unexpect_t, std::initializer_list<U> il,
669 Args&&... args) noexcept(noexcept(E(il, std::forward<Args>(args)...)))
670 : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
671
672 // helper ctor for transform()
673 template <class Fn, class... Args>
674 constexpr explicit storage_base(
676 Args&&... args) noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
677 std::forward<Args>(
678 args)...))))
679 : m_val(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
680 m_has_val(true) {}
681
682 // helper ctor for transform_error()
683 template <class Fn, class... Args>
684 constexpr explicit storage_base(
686 Args&&... args) noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
687 std::forward<Args>(
688 args)...))))
689 : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
690 m_has_val(false) {}
691
692 // Keep the exception specification implicit; an explicit noexcept trips
693 // Clang's LLVM-59854 during constexpr destruction.
694 ICEBERG_EXPECTED_CONSTEXPR_DTOR ~storage_base() {
695 if (m_has_val) {
696 if constexpr (!std::is_trivially_destructible_v<T>) {
697 m_val.~T();
698 }
699 } else {
700 if constexpr (!std::is_trivially_destructible_v<E>) {
701 m_unexpect.~E();
702 }
703 }
704 }
705
706 union {
707 T m_val;
708 E m_unexpect;
709 char m_dummy;
710 };
711 bool m_has_val;
712};
713
714// `T` is `void`, `E` is trivially-destructible
715template <class E>
716struct storage_base<void, E, true> {
717 storage_base(storage_base const&) = default;
718 storage_base(storage_base&&) = default;
719 storage_base& operator=(storage_base const&) = default;
720 storage_base& operator=(storage_base&&) = default;
721
722 constexpr storage_base() noexcept : m_has_val(true) {}
723
724 template <class Rhs>
725 constexpr storage_base(construct_from_expected_t, Rhs&& rhs) noexcept(
726 std::is_nothrow_constructible_v<
727 E, decltype(expected_detail::expected_source_error(std::declval<Rhs>()))>)
728 : m_val(), m_has_val(expected_detail::expected_source_has_value(rhs)) {
729 if (!m_has_val) {
730 expected_detail::construct_at(
731 std::addressof(m_unexpect),
732 expected_detail::expected_source_error(std::forward<Rhs>(rhs)));
733 }
734 }
735
736 constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
737
738 template <class... Args,
739 std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
740 constexpr explicit storage_base(unexpect_t, Args&&... args)
741 : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
742
743 template <class U, class... Args,
744 std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
745 Args&&...>>* = nullptr>
746 constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
747 : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
748
749 // helper ctor for transform_error()
750 template <class Fn, class... Args>
751 constexpr explicit storage_base(
753 Args&&... args) noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
754 std::forward<Args>(
755 args)...))))
756 : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
757 m_has_val(false) {}
758
759 ~storage_base() = default;
760
761 struct dummy {};
762
763 union {
764 E m_unexpect;
765 dummy m_val;
766 };
767
768 bool m_has_val;
769};
770
771// `T` is `void`, `E` is not trivially-destructible
772template <class E>
773struct storage_base<void, E, false> {
774 storage_base(storage_base const&) = default;
775 storage_base(storage_base&&) = default;
776 storage_base& operator=(storage_base const&) = default;
777 storage_base& operator=(storage_base&&) = default;
778
779 constexpr storage_base() noexcept : m_has_val(true) {}
780
781 template <class Rhs>
782 constexpr storage_base(construct_from_expected_t, Rhs&& rhs) noexcept(
783 std::is_nothrow_constructible_v<
784 E, decltype(expected_detail::expected_source_error(std::declval<Rhs>()))>)
785 : m_val(), m_has_val(expected_detail::expected_source_has_value(rhs)) {
786 if (!m_has_val) {
787 expected_detail::construct_at(
788 std::addressof(m_unexpect),
789 expected_detail::expected_source_error(std::forward<Rhs>(rhs)));
790 }
791 }
792
793 constexpr explicit storage_base(std::in_place_t) noexcept : m_has_val(true) {}
794
795 template <class... Args,
796 std::enable_if_t<std::is_constructible_v<E, Args&&...>>* = nullptr>
797 constexpr explicit storage_base(unexpect_t, Args&&... args)
798 : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {}
799
800 template <class U, class... Args,
801 std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
802 Args&&...>>* = nullptr>
803 constexpr explicit storage_base(unexpect_t, std::initializer_list<U> il, Args&&... args)
804 : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {}
805
806 // helper ctor for transform_error()
807 template <class Fn, class... Args>
808 constexpr explicit storage_base(
810 Args&&... args) noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
811 std::forward<Args>(
812 args)...))))
813 : m_unexpect(std::invoke(std::forward<Fn>(func), std::forward<Args>(args)...)),
814 m_has_val(false) {}
815
816 // Keep the exception specification implicit; an explicit noexcept trips
817 // Clang's LLVM-59854 during constexpr destruction.
818 ICEBERG_EXPECTED_CONSTEXPR_DTOR ~storage_base() {
819 if (!m_has_val) {
820 m_unexpect.~E();
821 }
822 }
823
824 struct dummy {};
825
826 union {
827 E m_unexpect;
828 dummy m_val;
829 };
830
831 bool m_has_val;
832};
833
834// This base class provides some handy member functions which can be used in
835// further derived classes
836template <class T, class E>
838 using storage_base<T, E>::storage_base;
839
840 constexpr T& get() & noexcept { return this->m_val; }
841 constexpr const T& get() const& noexcept { return this->m_val; }
842 constexpr T&& get() && noexcept(std::is_nothrow_move_constructible_v<T>) {
843 return std::move(this->m_val);
844 }
845 constexpr const T&& get() const&& noexcept(std::is_nothrow_move_constructible_v<T>) {
846 return std::move(this->m_val);
847 }
848
849 constexpr E& geterr() & noexcept { return this->m_unexpect; }
850 constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
851 constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
852 return std::move(this->m_unexpect);
853 }
854 constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
855 return std::move(this->m_unexpect);
856 }
857};
858
859// This base class provides some handy member functions which can be used in
860// further derived classes
861template <class E>
862struct operations_base<void, E> : storage_base<void, E> {
863 using storage_base<void, E>::storage_base;
864
865 constexpr E& geterr() & noexcept { return this->m_unexpect; }
866 constexpr const E& geterr() const& noexcept { return this->m_unexpect; }
867 constexpr E&& geterr() && noexcept(std::is_nothrow_move_constructible_v<E>) {
868 return std::move(this->m_unexpect);
869 }
870 constexpr const E&& geterr() const&& noexcept(std::is_nothrow_move_constructible_v<E>) {
871 return std::move(this->m_unexpect);
872 }
873};
874
875// This class manages conditionally having a trivial copy constructor
876// This specialization is for when T and E are trivially copy constructible
877template <class T, class E,
878 bool Enabled =
879 is_copy_constructible_or_void_v<T> && std::is_copy_constructible_v<E>,
880 bool Trivially = is_trivially_copy_constructible_or_void_v<T> &&
881 std::is_trivially_copy_constructible_v<E>>
883 using operations_base<T, E>::operations_base;
884};
885
886// This specialization is for when T or E are not copy constructible
887template <class T, class E>
888struct copy_ctor_base<T, E, false, false> : operations_base<T, E> {
889 using operations_base<T, E>::operations_base;
890
891 ~copy_ctor_base() = default;
892 copy_ctor_base() = default;
893 copy_ctor_base(const copy_ctor_base& rhs) = delete;
894 copy_ctor_base(copy_ctor_base&& rhs) = default;
895 copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
896 copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
897};
898
899// This specialization is for when T or E are not trivially copy constructible
900template <class T, class E>
901struct copy_ctor_base<T, E, true, false> : operations_base<T, E> {
902 using operations_base<T, E>::operations_base;
903
904 ~copy_ctor_base() = default;
905 copy_ctor_base() = default;
906
907 constexpr copy_ctor_base(const copy_ctor_base& rhs) noexcept(
908 is_nothrow_copy_constructible_or_void_v<T> &&
909 std::is_nothrow_copy_constructible_v<E>)
910 : operations_base<T, E>(construct_from_expected, rhs) {}
911
912 copy_ctor_base(copy_ctor_base&& rhs) = default;
913 copy_ctor_base& operator=(const copy_ctor_base& rhs) = default;
914 copy_ctor_base& operator=(copy_ctor_base&& rhs) = default;
915};
916
917// This class manages conditionally having a trivial move constructor
918template <class T, class E,
919 bool Enabled =
920 is_move_constructible_or_void_v<T> && std::is_move_constructible_v<E>,
921 bool Trivially = is_trivially_move_constructible_or_void_v<T> &&
922 std::is_trivially_move_constructible_v<E>>
924 using copy_ctor_base<T, E>::copy_ctor_base;
925};
926
927// This specialization is for when T or E are not move constructible
928template <class T, class E>
929struct move_ctor_base<T, E, false, false> : copy_ctor_base<T, E> {
930 using copy_ctor_base<T, E>::copy_ctor_base;
931
932 ~move_ctor_base() = default;
933 move_ctor_base() = default;
934 move_ctor_base(const move_ctor_base& rhs) = default;
935 move_ctor_base(move_ctor_base&& rhs) = delete;
936 move_ctor_base& operator=(const move_ctor_base& rhs) = default;
937 move_ctor_base& operator=(move_ctor_base&& rhs) = default;
938};
939
940// This specialization is for when T or E are not trivially move constructible
941template <class T, class E>
942struct move_ctor_base<T, E, true, false> : copy_ctor_base<T, E> {
943 using copy_ctor_base<T, E>::copy_ctor_base;
944
945 ~move_ctor_base() = default;
946 move_ctor_base() = default;
947 move_ctor_base(const move_ctor_base& rhs) = default;
948
949 constexpr move_ctor_base(move_ctor_base&& rhs) noexcept(
950 is_nothrow_move_constructible_or_void_v<T> &&
951 std::is_nothrow_move_constructible_v<E>)
952 : copy_ctor_base<T, E>(construct_from_expected, std::move(rhs)) {}
953
954 move_ctor_base& operator=(const move_ctor_base& rhs) = default;
955 move_ctor_base& operator=(move_ctor_base&& rhs) = default;
956};
957
958template <class T, class E>
959inline constexpr bool is_expected_copy_assignable_v =
960 is_copy_assignable_or_void_v<T> && is_copy_constructible_or_void_v<T> &&
961 std::is_copy_assignable_v<E> && std::is_copy_constructible_v<E> &&
962 (is_nothrow_move_constructible_or_void_v<T> ||
963 std::is_nothrow_move_constructible_v<E>);
964
965// LWG-4026
966template <class T, class E>
967inline constexpr bool is_expected_trivially_copy_assignable_v =
968 is_trivially_copy_constructible_or_void_v<T> &&
969 is_trivially_copy_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
970 std::is_trivially_copy_constructible_v<E> && std::is_trivially_copy_assignable_v<E> &&
971 std::is_trivially_destructible_v<E>;
972
973// This class manages conditionally having a (possibly trivial) copy assignment operator
974template <class T, class E, bool Enabled = is_expected_copy_assignable_v<T, E>,
975 bool Trivially = is_expected_trivially_copy_assignable_v<T, E>>
977 using move_ctor_base<T, E>::move_ctor_base;
978
979 ~copy_assign_base() = default;
980 copy_assign_base() = default;
981 copy_assign_base(const copy_assign_base& rhs) = default;
982 copy_assign_base(copy_assign_base&& rhs) = default;
983 copy_assign_base& operator=(const copy_assign_base& rhs) = default;
984 copy_assign_base& operator=(copy_assign_base&& rhs) = default;
985};
986
987template <class T, class E>
988struct copy_assign_base<T, E, false, false> : move_ctor_base<T, E> {
989 using move_ctor_base<T, E>::move_ctor_base;
990
991 ~copy_assign_base() = default;
992 copy_assign_base() = default;
993 copy_assign_base(const copy_assign_base& rhs) = default;
994 copy_assign_base(copy_assign_base&& rhs) = default;
995 copy_assign_base& operator=(const copy_assign_base& rhs) = delete;
996 copy_assign_base& operator=(copy_assign_base&& rhs) = default;
997};
998
999template <class T, class E>
1000struct copy_assign_base<T, E, true, false> : move_ctor_base<T, E> {
1001 using move_ctor_base<T, E>::move_ctor_base;
1002
1003 ~copy_assign_base() = default;
1004 copy_assign_base() = default;
1005 copy_assign_base(const copy_assign_base& rhs) = default;
1006 copy_assign_base(copy_assign_base&& rhs) = default;
1007
1008 constexpr copy_assign_base& operator=(const copy_assign_base& rhs) noexcept(
1009 std::is_nothrow_copy_constructible_v<T> && std::is_nothrow_copy_assignable_v<T> &&
1010 std::is_nothrow_copy_constructible_v<E> && std::is_nothrow_copy_assignable_v<E>) {
1011 if (this->m_has_val && rhs.m_has_val) {
1012 this->m_val = rhs.m_val;
1013 } else if (this->m_has_val) {
1014 expected_detail::reinit_expected(this->m_unexpect, this->m_val, rhs.m_unexpect);
1015 } else if (rhs.m_has_val) {
1016 expected_detail::reinit_expected(this->m_val, this->m_unexpect, rhs.m_val);
1017 } else {
1018 this->m_unexpect = rhs.m_unexpect;
1019 }
1020 this->m_has_val = rhs.m_has_val;
1021 return *this;
1022 }
1023
1024 copy_assign_base& operator=(copy_assign_base&& rhs) = default;
1025};
1026
1027template <class E>
1028struct copy_assign_base<void, E, true, false> : move_ctor_base<void, E> {
1029 using move_ctor_base<void, E>::move_ctor_base;
1030
1031 ~copy_assign_base() = default;
1032 copy_assign_base() = default;
1033 copy_assign_base(const copy_assign_base& rhs) = default;
1034 copy_assign_base(copy_assign_base&& rhs) = default;
1035
1036 constexpr copy_assign_base& operator=(const copy_assign_base& rhs) noexcept(
1037 std::is_nothrow_copy_constructible_v<E> && std::is_nothrow_copy_assignable_v<E>) {
1038 if (this->m_has_val && rhs.m_has_val) {
1039 // no-op
1040 } else if (this->m_has_val) {
1041 expected_detail::construct_at(std::addressof(this->m_unexpect), rhs.m_unexpect);
1042 this->m_has_val = false;
1043 } else if (rhs.m_has_val) {
1044 this->m_unexpect.~E();
1045 this->m_has_val = true;
1046 } else {
1047 this->m_unexpect = rhs.m_unexpect;
1048 }
1049 return *this;
1050 }
1051
1052 copy_assign_base& operator=(copy_assign_base&& rhs) = default;
1053};
1054
1055template <class T, class E>
1056inline constexpr bool is_expected_move_assignable_v =
1057 is_move_assignable_or_void_v<T> && is_move_constructible_or_void_v<T> &&
1058 std::is_move_assignable_v<E> && std::is_move_constructible_v<E> &&
1059 (is_nothrow_move_constructible_or_void_v<T> ||
1060 std::is_nothrow_move_constructible_v<E>);
1061
1062// LWG-4026
1063template <class T, class E>
1064inline constexpr bool is_expected_trivially_move_assignable_v =
1065 is_trivially_move_constructible_or_void_v<T> &&
1066 is_trivially_move_assignable_or_void_v<T> && is_trivially_destructible_or_void_v<T> &&
1067 std::is_trivially_move_constructible_v<E> && std::is_trivially_move_assignable_v<E> &&
1068 std::is_trivially_destructible_v<E>;
1069
1070// This class manages conditionally having a (possibly trivial) move assignment operator
1071template <class T, class E, bool Enabled = is_expected_move_assignable_v<T, E>,
1072 bool Trivially = is_expected_trivially_move_assignable_v<T, E>>
1074 using copy_assign_base<T, E>::copy_assign_base;
1075
1076 ~move_assign_base() = default;
1077 move_assign_base() = default;
1078 move_assign_base(const move_assign_base& rhs) = default;
1079 move_assign_base(move_assign_base&& rhs) = default;
1080 move_assign_base& operator=(const move_assign_base& rhs) = default;
1081 move_assign_base& operator=(move_assign_base&& rhs) = default;
1082};
1083
1084template <class T, class E>
1085struct move_assign_base<T, E, false, false> : copy_assign_base<T, E> {
1086 using copy_assign_base<T, E>::copy_assign_base;
1087
1088 ~move_assign_base() = default;
1089 move_assign_base() = default;
1090 move_assign_base(const move_assign_base& rhs) = default;
1091 move_assign_base(move_assign_base&& rhs) = default;
1092 move_assign_base& operator=(const move_assign_base& rhs) = default;
1093 move_assign_base& operator=(move_assign_base&& rhs) = delete;
1094};
1095
1096template <class T, class E>
1097struct move_assign_base<T, E, true, false> : copy_assign_base<T, E> {
1098 using copy_assign_base<T, E>::copy_assign_base;
1099
1100 ~move_assign_base() = default;
1101 move_assign_base() = default;
1102 move_assign_base(const move_assign_base& rhs) = default;
1103 move_assign_base(move_assign_base&& rhs) = default;
1104 move_assign_base& operator=(const move_assign_base& rhs) = default;
1105
1106 constexpr move_assign_base& operator=(move_assign_base&& rhs) noexcept(
1107 std::is_nothrow_move_constructible_v<T> && std::is_nothrow_move_assignable_v<T> &&
1108 std::is_nothrow_move_constructible_v<E> && std::is_nothrow_move_assignable_v<E>) {
1109 if (this->m_has_val && rhs.m_has_val) {
1110 this->m_val = std::move(rhs.m_val);
1111 } else if (this->m_has_val) {
1112 expected_detail::reinit_expected(this->m_unexpect, this->m_val,
1113 std::move(rhs.m_unexpect));
1114 } else if (rhs.m_has_val) {
1115 expected_detail::reinit_expected(this->m_val, this->m_unexpect,
1116 std::move(rhs.m_val));
1117 } else {
1118 this->m_unexpect = std::move(rhs.m_unexpect);
1119 }
1120 this->m_has_val = rhs.m_has_val;
1121 return *this;
1122 }
1123};
1124
1125template <class E>
1126struct move_assign_base<void, E, true, false> : copy_assign_base<void, E> {
1127 using copy_assign_base<void, E>::copy_assign_base;
1128
1129 ~move_assign_base() = default;
1130 move_assign_base() = default;
1131 move_assign_base(const move_assign_base& rhs) = default;
1132 move_assign_base(move_assign_base&& rhs) = default;
1133 move_assign_base& operator=(const move_assign_base& rhs) = default;
1134
1135 constexpr move_assign_base& operator=(move_assign_base&& rhs) noexcept(
1136 std::is_nothrow_move_constructible_v<E> && std::is_nothrow_move_assignable_v<E>) {
1137 if (this->m_has_val && rhs.m_has_val) {
1138 // no-op
1139 } else if (this->m_has_val) {
1140 expected_detail::construct_at(std::addressof(this->m_unexpect),
1141 std::move(rhs.m_unexpect));
1142 this->m_has_val = false;
1143 } else if (rhs.m_has_val) {
1144 this->m_unexpect.~E();
1145 this->m_has_val = true;
1146 } else {
1147 this->m_unexpect = std::move(rhs.m_unexpect);
1148 }
1149 return *this;
1150 }
1151};
1152
1153// This is needed to be able to construct the default_ctor_base which
1154// follows, while still conditionally deleting the default constructor.
1156 explicit default_constructor_tag() = default;
1157};
1158
1159// default_ctor_base will ensure that expected has a deleted default
1160// consturctor if T is not default constructible.
1161// This specialization is for when T is default constructible
1162template <class T, class E, bool Enable = is_default_constructible_or_void_v<T>>
1164 ~default_ctor_base() = default;
1165 default_ctor_base() = default;
1166 default_ctor_base(default_ctor_base const&) = default;
1168 default_ctor_base& operator=(default_ctor_base const&) = default;
1169 default_ctor_base& operator=(default_ctor_base&&) = default;
1170
1171 constexpr explicit default_ctor_base(default_constructor_tag) {}
1172};
1173
1174// This specialization is for when T is not default constructible
1175template <class T, class E>
1176struct default_ctor_base<T, E, false> {
1177 ~default_ctor_base() = default;
1178 default_ctor_base() = delete;
1179 default_ctor_base(default_ctor_base const&) = default;
1181 default_ctor_base& operator=(default_ctor_base const&) = default;
1182 default_ctor_base& operator=(default_ctor_base&&) = default;
1183
1184 constexpr explicit default_ctor_base(default_constructor_tag) {}
1185};
1186
1187} // namespace expected_detail
1188
1196template <class T, class E>
1197class ICEBERG_TEMPLATE_CLASS_EXPORT [[nodiscard]] expected
1198 : private expected_detail::move_assign_base<T, E>,
1200 static_assert(expected_detail::is_value_type_valid_v<T>);
1201 static_assert(expected_detail::is_error_type_valid_v<E>);
1202
1203 constexpr T* valptr() noexcept { return std::addressof(this->m_val); }
1204 constexpr const T* valptr() const noexcept { return std::addressof(this->m_val); }
1205 constexpr E* errptr() noexcept { return std::addressof(this->m_unexpect); }
1206 constexpr const E* errptr() const noexcept { return std::addressof(this->m_unexpect); }
1207
1208 constexpr T& val() noexcept { return this->m_val; }
1209 constexpr E& err() noexcept { return this->m_unexpect; }
1210
1211 constexpr const T& val() const noexcept { return this->m_val; }
1212 constexpr const E& err() const noexcept { return this->m_unexpect; }
1213
1216
1217 public:
1218 typedef T value_type;
1219 typedef E error_type;
1221
1222 template <class U>
1224
1225 // default constructors
1226
1227 constexpr expected() = default;
1228 constexpr expected(const expected& rhs) = default;
1229 constexpr expected(expected&& rhs) = default;
1230
1231 // constructors for expected<U, G>
1232
1233 // implicit const reference
1234 template <class U, class G,
1235 std::enable_if_t<!std::is_same_v<expected<U, G>, expected>>* = nullptr,
1236 std::enable_if_t<std::is_convertible_v<const U&, T> &&
1237 std::is_convertible_v<const G&, E>>* = nullptr,
1238 expected_detail::enable_from_other_expected_t<T, E, U, G, const U&,
1239 const G&>* = nullptr>
1240 constexpr expected(const expected<U, G>& rhs) noexcept(
1241 std::is_nothrow_constructible_v<T, const U&> &&
1242 std::is_nothrow_constructible_v<E, const G&>)
1243 : impl_base(expected_detail::construct_from_expected, rhs),
1245
1246 // explicit const reference
1247 template <class U, class G,
1248 std::enable_if_t<!std::is_same_v<expected<U, G>, expected>>* = nullptr,
1249 std::enable_if_t<!(std::is_convertible_v<const U&, T> &&
1250 std::is_convertible_v<const G&, E>)>* = nullptr,
1251 expected_detail::enable_from_other_expected_t<T, E, U, G, const U&,
1252 const G&>* = nullptr>
1253 constexpr explicit expected(const expected<U, G>& rhs) noexcept(
1254 std::is_nothrow_constructible_v<T, const U&> &&
1255 std::is_nothrow_constructible_v<E, const G&>)
1256 : impl_base(expected_detail::construct_from_expected, rhs),
1258
1259 // implicit rvalue
1260 template <class U, class G,
1261 std::enable_if_t<!std::is_same_v<expected<U, G>, expected>>* = nullptr,
1262 std::enable_if_t<std::is_convertible_v<U, T> &&
1263 std::is_convertible_v<G, E>>* = nullptr,
1264 expected_detail::enable_from_other_expected_t<T, E, U, G, U, G>* = nullptr>
1265 constexpr expected(expected<U, G>&& rhs) noexcept(
1266 std::is_nothrow_constructible_v<T, U> && std::is_nothrow_constructible_v<E, G>)
1267 : impl_base(expected_detail::construct_from_expected, std::move(rhs)),
1269
1270 // explicit rvalue
1271 template <class U, class G,
1272 std::enable_if_t<!std::is_same_v<expected<U, G>, expected>>* = nullptr,
1273 std::enable_if_t<!(std::is_convertible_v<U, T> &&
1274 std::is_convertible_v<G, E>)>* = nullptr,
1275 expected_detail::enable_from_other_expected_t<T, E, U, G, U, G>* = nullptr>
1276 constexpr explicit expected(expected<U, G>&& rhs) noexcept(
1277 std::is_nothrow_constructible_v<T, U> && std::is_nothrow_constructible_v<E, G>)
1278 : impl_base(expected_detail::construct_from_expected, std::move(rhs)),
1280
1281 // template <class U = T>
1282 // expected(U &&)
1283
1284 // implicit
1285 template <class U = std::remove_cv_t<T>,
1286 std::enable_if_t<std::is_convertible_v<U, T>>* = nullptr,
1287 expected_detail::enable_forward_t<T, E, U>* = nullptr>
1288 constexpr expected(U&& v) noexcept(std::is_nothrow_constructible_v<T, U>)
1289 : expected(std::in_place, std::forward<U>(v)) {}
1290
1291 // explicit
1292 template <class U = std::remove_cv_t<T>,
1293 std::enable_if_t<!std::is_convertible_v<U, T>>* = nullptr,
1294 expected_detail::enable_forward_t<T, E, U>* = nullptr>
1295 constexpr explicit expected(U&& v) noexcept(std::is_nothrow_constructible_v<T, U>)
1296 : expected(std::in_place, std::forward<U>(v)) {}
1297
1298#if !ICEBERG_CXX20_COMPAT && defined(__cpp_lib_expected) && __cpp_lib_expected >= 202202L
1299 // iceberg: library code built as C++23 still spells some errors std::unexpected.
1300 template <class G, std::enable_if_t<std::is_constructible_v<E, const G&>>* = nullptr>
1301 constexpr explicit(!std::is_convertible_v<const G&, E>) expected(
1302 const std::unexpected<G>& e) noexcept(std::is_nothrow_constructible_v<E, const G&>)
1303 : expected(unexpect, e.error()) {}
1304
1305 template <class G, std::enable_if_t<std::is_constructible_v<E, G>>* = nullptr>
1306 constexpr explicit(!std::is_convertible_v<G, E>)
1307 expected(std::unexpected<G>&& e) noexcept(std::is_nothrow_constructible_v<E, G>)
1308 : expected(unexpect, std::move(e.error())) {}
1309#endif
1310
1311 // constructors for unexpected<G>
1312
1313 // explicit const unexpected<G> &
1314 template <class G, std::enable_if_t<!std::is_convertible_v<const G&, E>>* = nullptr,
1315 std::enable_if_t<std::is_constructible_v<E, const G&>>* = nullptr>
1316 explicit constexpr expected(const unexpected<G>& e) noexcept(
1317 std::is_nothrow_constructible_v<E, const G&>)
1318 : impl_base(unexpect, e.error()),
1320
1321 // implicit const unexpected<G> &
1322 template <class G, std::enable_if_t<std::is_convertible_v<const G&, E>>* = nullptr,
1323 std::enable_if_t<std::is_constructible_v<E, const G&>>* = nullptr>
1324 constexpr expected(unexpected<G> const& e) noexcept(
1325 std::is_nothrow_constructible_v<E, const G&>)
1326 : impl_base(unexpect, e.error()),
1328
1329 // explicit unexpected<G> &&
1330 template <class G, std::enable_if_t<!std::is_convertible_v<G, E>>* = nullptr,
1331 std::enable_if_t<std::is_constructible_v<E, G>>* = nullptr>
1332 explicit constexpr expected(unexpected<G>&& e) noexcept(
1333 std::is_nothrow_constructible_v<E, G>)
1334 : impl_base(unexpect, std::move(e.error())),
1336
1337 // implicit unexpected<G> &&
1338 template <class G, std::enable_if_t<std::is_convertible_v<G, E>>* = nullptr,
1339 std::enable_if_t<std::is_constructible_v<E, G>>* = nullptr>
1340 constexpr expected(unexpected<G>&& e) noexcept(std::is_nothrow_constructible_v<E, G>)
1341 : impl_base(unexpect, std::move(e.error())),
1343
1344 // template<class... Args>
1345 // expected(std::in_place_t, Args &&...)
1346
1347 template <class... Args,
1348 std::enable_if_t<std::is_constructible_v<T, Args...>>* = nullptr>
1349 constexpr explicit expected(std::in_place_t, Args&&... args) noexcept(
1350 std::is_nothrow_constructible_v<T, Args...>)
1351 : impl_base(std::in_place, std::forward<Args>(args)...),
1353
1354 // template<class U, class... Args>
1355 // expected(std::in_place_t, std::initializer_list<U>, Args &&...)
1356
1357 template <class U, class... Args,
1358 std::enable_if_t<std::is_constructible_v<T, std::initializer_list<U>&,
1359 Args...>>* = nullptr>
1360 constexpr explicit expected(
1361 std::in_place_t, std::initializer_list<U> il,
1362 Args&&... args) noexcept(std::is_nothrow_constructible_v<T,
1363 std::initializer_list<U>&,
1364 Args...>)
1365 : impl_base(std::in_place, il, std::forward<Args>(args)...),
1367
1368 // helper ctor for transform()
1369 template <class Fn, class... Args>
1370 constexpr explicit expected(
1372 Args&&... args) noexcept(noexcept(static_cast<T>(std::invoke(std::forward<Fn>(func),
1373 std::forward<Args>(
1374 args)...))))
1375 : impl_base(tag, std::forward<Fn>(func), std::forward<Args>(args)...),
1377
1378 // template<class... Args>
1379 // expected(unexpect_t, Args &&...)
1380
1381 template <class... Args,
1382 std::enable_if_t<std::is_constructible_v<E, Args...>>* = nullptr>
1383 constexpr explicit expected(unexpect_t, Args&&... args) noexcept(
1384 std::is_nothrow_constructible_v<E, Args...>)
1385 : impl_base(unexpect, std::forward<Args>(args)...),
1387
1388 // template<class U, class... Args>
1389 // expected(unexpect_t, std::initializer_list<U>, Args &&...)
1390
1391 template <class U, class... Args,
1392 std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
1393 Args...>>* = nullptr>
1394 constexpr explicit expected(
1395 unexpect_t, std::initializer_list<U> il,
1396 Args&&... args) noexcept(std::is_nothrow_constructible_v<E,
1397 std::initializer_list<U>&,
1398 Args...>)
1399 : impl_base(unexpect, il, std::forward<Args>(args)...),
1401
1402 // helper ctor for transform_error()
1403 template <class Fn, class... Args>
1404 constexpr explicit expected(
1406 Args&&... args) noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
1407 std::forward<Args>(
1408 args)...))))
1409 : impl_base(tag1, tag2, std::forward<Fn>(func), std::forward<Args>(args)...),
1411
1412 // default assignment operators
1413
1414 constexpr expected& operator=(const expected& rhs) = default;
1415 constexpr expected& operator=(expected&& rhs) = default;
1416
1417 // assignments
1418
1419 template <
1420 class U = std::remove_cv_t<T>,
1421 std::enable_if_t<!std::is_same_v<expected, expected_detail::remove_cvref_t<U>> &&
1422 !expected_detail::is_specialization_v<
1423 expected_detail::remove_cvref_t<U>, unexpected> &&
1424 std::is_constructible_v<T, U> && std::is_assignable_v<T&, U> &&
1425 (std::is_nothrow_constructible_v<T, U> ||
1426 std::is_nothrow_move_constructible_v<T> ||
1427 std::is_nothrow_move_constructible_v<E>)>* = nullptr>
1428 constexpr expected& operator=(U&& v) noexcept(std::is_nothrow_constructible_v<T, U> &&
1429 std::is_nothrow_assignable_v<T&, U>) {
1430 if (has_value()) {
1431 val() = std::forward<U>(v);
1432 } else {
1433 expected_detail::reinit_expected(val(), err(), std::forward<U>(v));
1434 this->m_has_val = true;
1435 }
1436
1437 return *this;
1438 }
1439
1440 template <
1441 class G, class GF = const G&,
1442 std::enable_if_t<std::is_constructible_v<E, GF> && std::is_assignable_v<E&, GF> &&
1443 (std::is_nothrow_constructible_v<E, GF> ||
1444 std::is_nothrow_move_constructible_v<T> ||
1445 std::is_nothrow_move_constructible_v<E>)>* = nullptr>
1446 constexpr expected& operator=(const unexpected<G>& rhs) noexcept(
1447 std::is_nothrow_constructible_v<E, GF> && std::is_nothrow_assignable_v<E&, GF>) {
1448 if (!has_value()) {
1449 err() = std::forward<GF>(rhs.error());
1450 } else {
1451 expected_detail::reinit_expected(err(), val(), std::forward<GF>(rhs.error()));
1452 this->m_has_val = false;
1453 }
1454
1455 return *this;
1456 }
1457
1458 template <
1459 class G, class GF = G,
1460 std::enable_if_t<std::is_constructible_v<E, GF> && std::is_assignable_v<E&, GF> &&
1461 (std::is_nothrow_constructible_v<E, GF> ||
1462 std::is_nothrow_move_constructible_v<T> ||
1463 std::is_nothrow_move_constructible_v<E>)>* = nullptr>
1464 constexpr expected& operator=(unexpected<G>&& rhs) noexcept(
1465 std::is_nothrow_constructible_v<E, GF> && std::is_nothrow_assignable_v<E&, GF>) {
1466 if (!has_value()) {
1467 err() = std::forward<GF>(rhs.error());
1468 } else {
1469 expected_detail::reinit_expected(err(), val(), std::forward<GF>(rhs.error()));
1470 this->m_has_val = false;
1471 }
1472
1473 return *this;
1474 }
1475
1476 template <class... Args,
1477 typename std::enable_if_t<std::is_nothrow_constructible_v<T, Args&&...>,
1478 bool> = false>
1479 constexpr T& emplace(Args&&... args) {
1480 if (has_value()) {
1481 if constexpr (!std::is_trivially_destructible_v<T>) {
1482 val().~T();
1483 }
1484 } else {
1485 if constexpr (!std::is_trivially_destructible_v<E>) {
1486 err().~E();
1487 }
1488 this->m_has_val = true;
1489 }
1490
1491 return *expected_detail::construct_at(valptr(), std::forward<Args>(args)...);
1492 }
1493
1494 template <class U, class... Args,
1495 std::enable_if_t<std::is_nothrow_constructible_v<T, std::initializer_list<U>&,
1496 Args&&...>>* = nullptr>
1497 constexpr T& emplace(std::initializer_list<U> il, Args&&... args) {
1498 if (has_value()) {
1499 if constexpr (!std::is_trivially_destructible_v<T>) {
1500 val().~T();
1501 }
1502 } else {
1503 if constexpr (!std::is_trivially_destructible_v<E>) {
1504 err().~E();
1505 }
1506 this->m_has_val = true;
1507 }
1508
1509 return *expected_detail::construct_at(valptr(), il, std::forward<Args>(args)...);
1510 }
1511
1512 template <class OT = T, class OE = E>
1513 constexpr std::enable_if_t<std::is_swappable_v<OT> && std::is_swappable_v<OE> &&
1514 std::is_move_constructible_v<OT> &&
1515 std::is_move_constructible_v<OE> &&
1516 (std::is_nothrow_move_constructible_v<OT> ||
1517 std::is_nothrow_move_constructible_v<OE>)>
1518 swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v<T> &&
1519 std::is_nothrow_swappable_v<T> &&
1520 std::is_nothrow_move_constructible_v<E> &&
1521 std::is_nothrow_swappable_v<E>) {
1522 using std::swap;
1523 if (this->m_has_val && rhs.m_has_val) {
1524 swap(this->m_val, rhs.m_val); // ADL
1525 } else if (this->m_has_val) {
1526 if constexpr (std::is_nothrow_move_constructible_v<E>) {
1527 E tmp(std::move(rhs.error()));
1528 if constexpr (!std::is_trivially_destructible_v<E>) {
1529 rhs.error().~E();
1530 }
1531
1532 if constexpr (std::is_nothrow_move_constructible_v<T>) {
1533 expected_detail::construct_at(std::addressof(rhs.m_val),
1534 std::move(this->m_val));
1535 } else {
1536 expected_detail::ReinitGuard<E> guard{std::addressof(rhs.error()),
1537 std::addressof(tmp)};
1538 expected_detail::construct_at(std::addressof(rhs.m_val),
1539 std::move(this->m_val));
1540 guard._target = nullptr;
1541 }
1542
1543 if constexpr (!std::is_trivially_destructible_v<T>) {
1544 this->m_val.~T();
1545 }
1546 expected_detail::construct_at(std::addressof(this->error()), std::move(tmp));
1547 } else {
1548 T tmp(std::move(this->m_val));
1549 if constexpr (!std::is_trivially_destructible_v<T>) {
1550 this->m_val.~T();
1551 }
1552
1553 expected_detail::ReinitGuard<T> guard{std::addressof(this->m_val),
1554 std::addressof(tmp)};
1555 expected_detail::construct_at(std::addressof(this->error()),
1556 std::move(rhs.error()));
1557 guard._target = nullptr;
1558
1559 if constexpr (!std::is_trivially_destructible_v<E>) {
1560 rhs.error().~E();
1561 }
1562 expected_detail::construct_at(std::addressof(rhs.m_val), std::move(tmp));
1563 }
1564
1565 this->m_has_val = false;
1566 rhs.m_has_val = true;
1567 } else if (rhs.m_has_val) {
1568 rhs.swap(*this);
1569 } else {
1570 swap(this->error(), rhs.error()); // ADL
1571 }
1572 }
1573
1574 constexpr const T* operator->() const noexcept { return valptr(); }
1575 constexpr T* operator->() noexcept { return valptr(); }
1576
1577 constexpr const T& operator*() const& noexcept { return val(); }
1578 constexpr T& operator*() & noexcept { return val(); }
1579 constexpr const T&& operator*() const&& noexcept { return std::move(val()); }
1580 constexpr T&& operator*() && noexcept { return std::move(val()); }
1581
1582 constexpr bool has_value() const noexcept { return this->m_has_val; }
1583 constexpr explicit operator bool() const noexcept { return this->m_has_val; }
1584
1585 constexpr const T& value() const& {
1586 static_assert(std::is_copy_constructible_v<E>,
1587 "E must be copy constructible, by LWG-3843");
1588 if (!has_value()) ICEBERG_EXPECTED_THROW(bad_expected_access(error()));
1589 return val();
1590 }
1591 constexpr T& value() & {
1592 static_assert(std::is_copy_constructible_v<E>,
1593 "E must be copy constructible, by LWG-3843");
1594 if (!has_value()) ICEBERG_EXPECTED_THROW(bad_expected_access(std::as_const(error())));
1595 return val();
1596 }
1597 constexpr const T&& value() const&& {
1598 static_assert(std::is_copy_constructible_v<E>,
1599 "E must be copy constructible, by LWG-3843");
1600 static_assert(std::is_constructible_v<E, decltype(std::move(error()))>,
1601 "E must be constructible from const E&&, by LWG-3843");
1602 if (!has_value()) ICEBERG_EXPECTED_THROW(bad_expected_access(std::move(error())));
1603 return std::move(val());
1604 }
1605 constexpr T&& value() && {
1606 static_assert(std::is_copy_constructible_v<E>,
1607 "E must be copy constructible, by LWG-3843");
1608 static_assert(std::is_constructible_v<E, decltype(std::move(error()))>,
1609 "E must be constructible from E&&, by LWG-3843");
1610 if (!has_value()) ICEBERG_EXPECTED_THROW(bad_expected_access(std::move(error())));
1611 return std::move(val());
1612 }
1613
1614 constexpr const E& error() const& noexcept { return err(); }
1615 constexpr E& error() & noexcept { return err(); }
1616 constexpr const E&& error() const&& noexcept { return std::move(err()); }
1617 constexpr E&& error() && noexcept { return std::move(err()); }
1618
1619 template <class U = std::remove_cv_t<T>>
1620 constexpr T value_or(U&& v) const& noexcept(
1621 std::is_nothrow_copy_constructible_v<T> &&
1622 expected_detail::is_nothrow_convertible_v<U, T>) {
1623 static_assert(std::is_copy_constructible_v<T>, "T must be copy-constructible");
1624 static_assert(std::is_convertible_v<U, T>, "is_convertible_v<U, T> must be true");
1625 if (this->m_has_val) {
1626 return this->m_val;
1627 } else {
1628 return static_cast<T>(std::forward<U>(v));
1629 }
1630 }
1631 template <class U = std::remove_cv_t<T>>
1632 constexpr T value_or(U&& v) && noexcept(
1633 std::is_nothrow_move_constructible_v<T> &&
1634 expected_detail::is_nothrow_convertible_v<U, T>) {
1635 static_assert(std::is_move_constructible_v<T>, "T must be move-constructible");
1636 static_assert(std::is_convertible_v<U, T>, "is_convertible_v<U, T> must be true");
1637 if (this->m_has_val) {
1638 return std::move(this->m_val);
1639 } else {
1640 return static_cast<T>(std::forward<U>(v));
1641 }
1642 }
1643
1644 template <class G = E>
1645 constexpr E error_or(G&& v) const& noexcept(
1646 std::is_nothrow_copy_constructible_v<E> &&
1647 expected_detail::is_nothrow_convertible_v<G, E>) {
1648 static_assert(std::is_copy_constructible_v<E>, "E must be copy-constructible");
1649 static_assert(std::is_convertible_v<G, E>, "is_convertible_v<G, E> must be true");
1650 if (this->m_has_val) {
1651 return static_cast<E>(std::forward<G>(v));
1652 } else {
1653 return this->m_unexpect;
1654 }
1655 }
1656 template <class G = E>
1657 constexpr E error_or(G&& v) && noexcept(
1658 std::is_nothrow_move_constructible_v<E> &&
1659 expected_detail::is_nothrow_convertible_v<G, E>) {
1660 static_assert(std::is_move_constructible_v<E>, "E must be move-constructible");
1661 static_assert(std::is_convertible_v<G, E>, "is_convertible_v<G, E> must be true");
1662 if (this->m_has_val) {
1663 return static_cast<E>(std::forward<G>(v));
1664 } else {
1665 return std::move(this->m_unexpect);
1666 }
1667 }
1668
1669 template <class F, class GE = E,
1670 std::enable_if_t<std::is_constructible_v<GE, GE&>>* = nullptr>
1671 constexpr auto and_then(F&& f) & {
1672 using U =
1673 expected_detail::remove_cvref_t<std::invoke_result_t<F, decltype((this->m_val))>>;
1674 static_assert(expected_detail::is_specialization_v<U, expected>,
1675 "U (return type of F) must be specialization of expected");
1676 static_assert(std::is_same_v<typename U::error_type, E>,
1677 "The error type must be the same after calling the F");
1678
1679 if (has_value())
1680 return std::invoke(std::forward<F>(f), this->m_val);
1681 else
1682 return U(unexpect, error());
1683 }
1684 template <class F, class GE = E,
1685 std::enable_if_t<std::is_constructible_v<GE, const GE&>>* = nullptr>
1686 constexpr auto and_then(F&& f) const& {
1687 using U =
1688 expected_detail::remove_cvref_t<std::invoke_result_t<F, decltype((this->m_val))>>;
1689 static_assert(expected_detail::is_specialization_v<U, expected>,
1690 "U (return type of F) must be specialization of expected");
1691 static_assert(std::is_same_v<typename U::error_type, E>,
1692 "The error type must be the same after calling the F");
1693
1694 if (has_value())
1695 return std::invoke(std::forward<F>(f), this->m_val);
1696 else
1697 return U(unexpect, error());
1698 }
1699 template <class F, class GE = E,
1700 std::enable_if_t<std::is_constructible_v<GE, GE&&>>* = nullptr>
1701 constexpr auto and_then(F&& f) && {
1702 using U = expected_detail::remove_cvref_t<
1703 std::invoke_result_t<F, decltype(std::move(this->m_val))>>;
1704 static_assert(expected_detail::is_specialization_v<U, expected>,
1705 "U (return type of F) must be specialization of expected");
1706 static_assert(std::is_same_v<typename U::error_type, E>,
1707 "The error type must be the same after calling the F");
1708
1709 if (has_value())
1710 return std::invoke(std::forward<F>(f), std::move(this->m_val));
1711 else
1712 return U(unexpect, std::move(error()));
1713 }
1714 template <class F, class GE = E,
1715 std::enable_if_t<std::is_constructible_v<GE, const GE>>* = nullptr>
1716 constexpr auto and_then(F&& f) const&& {
1717 using U = expected_detail::remove_cvref_t<
1718 std::invoke_result_t<F, decltype(std::move(this->m_val))>>;
1719 static_assert(expected_detail::is_specialization_v<U, expected>,
1720 "U (return type of F) must be specialization of expected");
1721 static_assert(std::is_same_v<typename U::error_type, E>,
1722 "The error type must be the same after calling the F");
1723
1724 if (has_value())
1725 return std::invoke(std::forward<F>(f), std::move(this->m_val));
1726 else
1727 return U(unexpect, std::move(error()));
1728 }
1729
1730 template <class F, class UT = T,
1731 std::enable_if_t<std::is_constructible_v<UT, UT&>>* = nullptr>
1732 constexpr auto or_else(F&& f) & {
1733 using G = expected_detail::remove_cvref_t<std::invoke_result_t<F, decltype(error())>>;
1734 static_assert(expected_detail::is_specialization_v<G, expected>,
1735 "G (return type of F) must be specialization of expected");
1736 static_assert(std::is_same_v<typename G::value_type, T>,
1737 "The value type must be the same after calling the F");
1738
1739 if (has_value())
1740 return G(std::in_place, this->m_val);
1741 else
1742 return std::invoke(std::forward<F>(f), error());
1743 }
1744 template <class F, class UT = T,
1745 std::enable_if_t<std::is_constructible_v<UT, const UT&>>* = nullptr>
1746 constexpr auto or_else(F&& f) const& {
1747 using G = expected_detail::remove_cvref_t<std::invoke_result_t<F, decltype(error())>>;
1748 static_assert(expected_detail::is_specialization_v<G, expected>,
1749 "G (return type of F) must be specialization of expected");
1750 static_assert(std::is_same_v<typename G::value_type, T>,
1751 "The value type must be the same after calling the F");
1752
1753 if (has_value())
1754 return G(std::in_place, this->m_val);
1755 else
1756 return std::invoke(std::forward<F>(f), error());
1757 }
1758 template <class F, class UT = T,
1759 std::enable_if_t<std::is_constructible_v<UT, UT&&>>* = nullptr>
1760 constexpr auto or_else(F&& f) && {
1761 using G = expected_detail::remove_cvref_t<
1762 std::invoke_result_t<F, decltype(std::move(error()))>>;
1763 static_assert(expected_detail::is_specialization_v<G, expected>,
1764 "G (return type of F) must be specialization of expected");
1765 static_assert(std::is_same_v<typename G::value_type, T>,
1766 "The value type must be the same after calling the F");
1767
1768 if (has_value())
1769 return G(std::in_place, std::move(this->m_val));
1770 else
1771 return std::invoke(std::forward<F>(f), std::move(error()));
1772 }
1773 template <class F, class UT = T,
1774 std::enable_if_t<std::is_constructible_v<UT, const UT>>* = nullptr>
1775 constexpr auto or_else(F&& f) const&& {
1776 using G = expected_detail::remove_cvref_t<
1777 std::invoke_result_t<F, decltype(std::move(error()))>>;
1778 static_assert(expected_detail::is_specialization_v<G, expected>,
1779 "G (return type of F) must be specialization of expected");
1780 static_assert(std::is_same_v<typename G::value_type, T>,
1781 "The value type must be the same after calling the F");
1782
1783 if (has_value())
1784 return G(std::in_place, std::move(this->m_val));
1785 else
1786 return std::invoke(std::forward<F>(f), std::move(error()));
1787 }
1788
1789 template <class F, class GE = E,
1790 std::enable_if_t<std::is_constructible_v<GE, GE&>>* = nullptr>
1791 constexpr auto transform(F&& f) & {
1792 using U =
1793 expected_detail::remove_cvref_t<std::invoke_result_t<F, decltype((this->m_val))>>;
1794 static_assert(expected_detail::is_value_type_valid_v<U>,
1795 "U must be a valid type for expected<U, E>");
1796 // FIXME another constraint needed here
1797 if (!has_value()) {
1798 return expected<U, E>(unexpect, error());
1799 } else {
1800 if constexpr (std::is_void_v<U>) {
1801 std::invoke(std::forward<F>(f), this->m_val);
1802 return expected<U, E>{};
1803 } else {
1805 std::forward<F>(f), this->m_val);
1806 }
1807 }
1808 }
1809 template <class F, class GE = E,
1810 std::enable_if_t<std::is_constructible_v<GE, const GE&>>* = nullptr>
1811 constexpr auto transform(F&& f) const& {
1812 using U =
1813 expected_detail::remove_cvref_t<std::invoke_result_t<F, decltype((this->m_val))>>;
1814 static_assert(expected_detail::is_value_type_valid_v<U>,
1815 "U must be a valid type for expected<U, E>");
1816 // FIXME another constraint needed here
1817 if (!has_value()) {
1818 return expected<U, E>(unexpect, error());
1819 } else {
1820 if constexpr (std::is_void_v<U>) {
1821 std::invoke(std::forward<F>(f), this->m_val);
1822 return expected<U, E>{};
1823 } else {
1825 std::forward<F>(f), this->m_val);
1826 }
1827 }
1828 }
1829 template <class F, class GE = E,
1830 std::enable_if_t<std::is_constructible_v<GE, GE&&>>* = nullptr>
1831 constexpr auto transform(F&& f) && {
1832 using U = expected_detail::remove_cvref_t<
1833 std::invoke_result_t<F, decltype(std::move(this->m_val))>>;
1834 static_assert(expected_detail::is_value_type_valid_v<U>,
1835 "U must be a valid type for expected<U, E>");
1836 // FIXME another constraint needed here
1837 if (!has_value()) {
1838 return expected<U, E>(unexpect, std::move(error()));
1839 } else {
1840 if constexpr (std::is_void_v<U>) {
1841 std::invoke(std::forward<F>(f), std::move(this->m_val));
1842 return expected<U, E>{};
1843 } else {
1845 std::forward<F>(f), std::move(this->m_val));
1846 }
1847 }
1848 }
1849 template <class F, class GE = E,
1850 std::enable_if_t<std::is_constructible_v<GE, const GE>>* = nullptr>
1851 constexpr auto transform(F&& f) const&& {
1852 using U = expected_detail::remove_cvref_t<
1853 std::invoke_result_t<F, decltype(std::move(this->m_val))>>;
1854 static_assert(expected_detail::is_value_type_valid_v<U>,
1855 "U must be a valid type for expected<U, E>");
1856 // FIXME another constraint needed here
1857 if (!has_value()) {
1858 return expected<U, E>(unexpect, std::move(error()));
1859 } else {
1860 if constexpr (std::is_void_v<U>) {
1861 std::invoke(std::forward<F>(f), std::move(this->m_val));
1862 return expected<U, E>{};
1863 } else {
1865 std::forward<F>(f), std::move(this->m_val));
1866 }
1867 }
1868 }
1869
1870 template <class F, class UT = T,
1871 std::enable_if_t<std::is_constructible_v<UT, UT&>>* = nullptr>
1872 constexpr auto transform_error(F&& f) & {
1873 using G = expected_detail::remove_cvref_t<std::invoke_result_t<F, decltype(error())>>;
1874 static_assert(expected_detail::is_error_type_valid_v<G>,
1875 "G must be a valid type for expected<T, G>");
1876 // FIXME another constraint needed here
1877 if (has_value()) {
1878 return expected<T, G>(std::in_place, this->m_val);
1879 } else {
1881 std::forward<F>(f), error());
1882 }
1883 }
1884 template <class F, class UT = T,
1885 std::enable_if_t<std::is_constructible_v<UT, const UT&>>* = nullptr>
1886 constexpr auto transform_error(F&& f) const& {
1887 using G = expected_detail::remove_cvref_t<std::invoke_result_t<F, decltype(error())>>;
1888 static_assert(expected_detail::is_error_type_valid_v<G>,
1889 "G must be a valid type for expected<T, G>");
1890 // FIXME another constraint needed here
1891 if (has_value()) {
1892 return expected<T, G>(std::in_place, this->m_val);
1893 } else {
1895 std::forward<F>(f), error());
1896 }
1897 }
1898 template <class F, class UT = T,
1899 std::enable_if_t<std::is_constructible_v<UT, UT&&>>* = nullptr>
1900 constexpr auto transform_error(F&& f) && {
1901 using G = expected_detail::remove_cvref_t<
1902 std::invoke_result_t<F, decltype(std::move(error()))>>;
1903 static_assert(expected_detail::is_error_type_valid_v<G>,
1904 "G must be a valid type for expected<T, G>");
1905 // FIXME another constraint needed here
1906 if (has_value()) {
1907 return expected<T, G>(std::in_place, std::move(this->m_val));
1908 } else {
1910 std::forward<F>(f), std::move(error()));
1911 }
1912 }
1913 template <class F, class UT = T,
1914 std::enable_if_t<std::is_constructible_v<UT, const UT>>* = nullptr>
1915 constexpr auto transform_error(F&& f) const&& {
1916 using G = expected_detail::remove_cvref_t<
1917 std::invoke_result_t<F, decltype(std::move(error()))>>;
1918 static_assert(expected_detail::is_error_type_valid_v<G>,
1919 "G must be a valid type for expected<T, G>");
1920 // FIXME another constraint needed here
1921 if (has_value()) {
1922 return expected<T, G>(std::in_place, std::move(this->m_val));
1923 } else {
1925 std::forward<F>(f), std::move(error()));
1926 }
1927 }
1928
1929 template <class T2, class E2>
1930 [[nodiscard]] friend constexpr std::enable_if_t<
1931 !std::is_void_v<T2> &&
1932 expected_detail::is_equality_result_convertible_to_bool_v<T, T2> &&
1933 expected_detail::is_equality_result_convertible_to_bool_v<E, E2>,
1934 bool>
1935 operator==(const expected& x, const expected<T2, E2>& y) noexcept(
1936 expected_detail::is_nothrow_equality_result_convertible_to_bool_v<T, T2> &&
1937 expected_detail::is_nothrow_equality_result_convertible_to_bool_v<E, E2>) {
1938 if (x.has_value() != y.has_value()) {
1939 return false;
1940 } else if (x.has_value()) {
1941 return *x == *y;
1942 } else {
1943 return x.error() == y.error();
1944 }
1945 }
1946#if ICEBERG_EXPECTED_CPLUSPLUS < 202002L
1947 template <class T2, class E2>
1948 [[nodiscard]] friend constexpr std::enable_if_t<
1949 !std::is_void_v<T2> &&
1950 expected_detail::is_equality_result_convertible_to_bool_v<T, T2> &&
1951 expected_detail::is_equality_result_convertible_to_bool_v<E, E2>,
1952 bool> operator!=(const expected& x,
1953 const expected<T2, E2>& y) noexcept(noexcept(x == y)) {
1954 return !(x == y);
1955 }
1956#endif
1957
1958 template <class T2>
1959 [[nodiscard]] friend constexpr std::enable_if_t<
1960 !expected_detail::is_specialization_v<T2, iceberg::expected> &&
1961 expected_detail::is_equality_result_convertible_to_bool_v<T, T2>,
1962 bool>
1963 operator==(const expected& x, const T2& v) noexcept(
1964 expected_detail::is_nothrow_equality_result_convertible_to_bool_v<T, T2>) {
1965 if (x.has_value()) {
1966 return *x == v;
1967 } else {
1968 return false;
1969 }
1970 }
1971#if ICEBERG_EXPECTED_CPLUSPLUS < 202002L
1972 template <class T2>
1973 [[nodiscard]] friend constexpr std::enable_if_t<
1974 !expected_detail::is_specialization_v<T2, iceberg::expected> &&
1975 expected_detail::is_equality_result_convertible_to_bool_v<T, T2>,
1976 bool> operator!=(const expected& x, const T2& v) noexcept(noexcept(x == v)) {
1977 return !(x == v);
1978 }
1979#endif
1980
1981 template <class E2>
1982 [[nodiscard]] friend constexpr std::enable_if_t<
1983 expected_detail::is_equality_result_convertible_to_bool_v<E, E2>, bool>
1984 operator==(const expected& x, const unexpected<E2>& e) noexcept(
1985 expected_detail::is_nothrow_equality_result_convertible_to_bool_v<E, E2>) {
1986 if (x.has_value()) {
1987 return false;
1988 } else {
1989 return x.error() == e.error();
1990 }
1991 }
1992#if ICEBERG_EXPECTED_CPLUSPLUS < 202002L
1993 template <class E2>
1994 [[nodiscard]] friend constexpr std::enable_if_t<
1995 expected_detail::is_equality_result_convertible_to_bool_v<E, E2>, bool>
1996 operator!=(const expected& x, const unexpected<E2>& e) noexcept(noexcept(x == e)) {
1997 return !(x == e);
1998 }
1999#endif
2000};
2001
2002// standalone swap for non-void value type
2003template <class T, class E,
2004 std::enable_if_t<!std::is_void_v<T> && std::is_move_constructible_v<T> &&
2005 std::is_swappable_v<T> && std::is_move_constructible_v<E> &&
2006 std::is_swappable_v<E> &&
2007 (std::is_nothrow_move_constructible_v<T> ||
2008 std::is_nothrow_move_constructible_v<E>)>* = nullptr>
2009constexpr void swap(expected<T, E>& lhs,
2010 expected<T, E>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
2011 lhs.swap(rhs);
2012}
2013
2014template <class E>
2015class ICEBERG_EXPORT [[nodiscard]]
2018 static_assert(expected_detail::is_error_type_valid_v<E>);
2019
2020 using T = void;
2021
2022 constexpr E* errptr() noexcept { return std::addressof(this->m_unexpect); }
2023 constexpr const E* errptr() const noexcept { return std::addressof(this->m_unexpect); }
2024
2025 constexpr void val() noexcept {}
2026
2027 constexpr E& err() noexcept { return this->m_unexpect; }
2028
2029 constexpr void val() const noexcept {}
2030
2031 constexpr const E& err() const noexcept { return this->m_unexpect; }
2032
2035
2036 public:
2037 typedef T value_type;
2038 typedef E error_type;
2040
2041 template <class U>
2043
2044 constexpr expected() = default;
2045 constexpr expected(const expected& rhs) = default;
2046 constexpr expected(expected&& rhs) = default;
2047
2048 template <
2049 class U, class G,
2050 std::enable_if_t<!std::is_same_v<expected<U, G>, expected>>* = nullptr,
2051 std::enable_if_t<std::is_convertible_v<const G&, E>>* = nullptr,
2052 expected_detail::enable_from_other_void_expected_t<E, U, G, const G&>* = nullptr>
2053 constexpr expected(const expected<U, G>& rhs) noexcept(
2054 std::is_nothrow_constructible_v<E, const G&>)
2055 : impl_base(expected_detail::construct_from_expected, rhs),
2057
2058 template <
2059 class U, class G,
2060 std::enable_if_t<!std::is_same_v<expected<U, G>, expected>>* = nullptr,
2061 std::enable_if_t<!std::is_convertible_v<const G&, E>>* = nullptr,
2062 expected_detail::enable_from_other_void_expected_t<E, U, G, const G&>* = nullptr>
2063 constexpr explicit expected(const expected<U, G>& rhs) noexcept(
2064 std::is_nothrow_constructible_v<E, const G&>)
2065 : impl_base(expected_detail::construct_from_expected, rhs),
2067
2068 template <class U, class G,
2069 std::enable_if_t<!std::is_same_v<expected<U, G>, expected>>* = nullptr,
2070 std::enable_if_t<std::is_convertible_v<G, E>>* = nullptr,
2071 expected_detail::enable_from_other_void_expected_t<E, U, G, G>* = nullptr>
2072 constexpr expected(expected<U, G>&& rhs) noexcept(std::is_nothrow_constructible_v<E, G>)
2073 : impl_base(expected_detail::construct_from_expected, std::move(rhs)),
2075
2076 template <class U, class G,
2077 std::enable_if_t<!std::is_same_v<expected<U, G>, expected>>* = nullptr,
2078 std::enable_if_t<!std::is_convertible_v<G, E>>* = nullptr,
2079 expected_detail::enable_from_other_void_expected_t<E, U, G, G>* = nullptr>
2080 constexpr explicit expected(expected<U, G>&& rhs) noexcept(
2081 std::is_nothrow_constructible_v<E, G>)
2082 : impl_base(expected_detail::construct_from_expected, std::move(rhs)),
2084
2085#if !ICEBERG_CXX20_COMPAT && defined(__cpp_lib_expected) && __cpp_lib_expected >= 202202L
2086 // iceberg: library code built as C++23 still spells some errors std::unexpected.
2087 template <class G, std::enable_if_t<std::is_constructible_v<E, const G&>>* = nullptr>
2088 constexpr explicit(!std::is_convertible_v<const G&, E>) expected(
2089 const std::unexpected<G>& e) noexcept(std::is_nothrow_constructible_v<E, const G&>)
2090 : expected(unexpect, e.error()) {}
2091
2092 template <class G, std::enable_if_t<std::is_constructible_v<E, G>>* = nullptr>
2093 constexpr explicit(!std::is_convertible_v<G, E>)
2094 expected(std::unexpected<G>&& e) noexcept(std::is_nothrow_constructible_v<E, G>)
2095 : expected(unexpect, std::move(e.error())) {}
2096#endif
2097
2098 // constructors for unexpected<G>
2099
2100 // explicit const unexpected<G> &
2101 template <class G, std::enable_if_t<!std::is_convertible_v<const G&, E>>* = nullptr,
2102 std::enable_if_t<std::is_constructible_v<E, const G&>>* = nullptr>
2103 explicit constexpr expected(const unexpected<G>& e) noexcept(
2104 std::is_nothrow_constructible_v<E, const G&>)
2105 : impl_base(unexpect, e.error()),
2107
2108 // implicit const unexpected<G> &
2109 template <class G, std::enable_if_t<std::is_convertible_v<const G&, E>>* = nullptr,
2110 std::enable_if_t<std::is_constructible_v<E, const G&>>* = nullptr>
2111 constexpr expected(unexpected<G> const& e) noexcept(
2112 std::is_nothrow_constructible_v<E, const G&>)
2113 : impl_base(unexpect, e.error()),
2115
2116 // explicit unexpected<G> &&
2117 template <class G, std::enable_if_t<!std::is_convertible_v<G, E>>* = nullptr,
2118 std::enable_if_t<std::is_constructible_v<E, G>>* = nullptr>
2119 explicit constexpr expected(unexpected<G>&& e) noexcept(
2120 std::is_nothrow_constructible_v<E, G>)
2121 : impl_base(unexpect, std::move(e.error())),
2123
2124 // implicit unexpected<G> &&
2125 template <class G, std::enable_if_t<std::is_convertible_v<G, E>>* = nullptr,
2126 std::enable_if_t<std::is_constructible_v<E, G>>* = nullptr>
2127 constexpr expected(unexpected<G>&& e) noexcept(std::is_nothrow_constructible_v<E, G>)
2128 : impl_base(unexpect, std::move(e.error())),
2130
2131 // expected(std::in_place_t)
2132 constexpr explicit expected(std::in_place_t) noexcept
2134
2135 // template<class... Args>
2136 // expected(unexpect_t, Args &&...)
2137 template <class... Args,
2138 std::enable_if_t<std::is_constructible_v<E, Args...>>* = nullptr>
2139 constexpr explicit expected(unexpect_t, Args&&... args) noexcept(
2140 std::is_nothrow_constructible_v<E, Args...>)
2141 : impl_base(unexpect, std::forward<Args>(args)...),
2143
2144 // template<class U, class... Args>
2145 // expected(unexpect_t, std::initializer_list<U>, Args &&...)
2146 template <class U, class... Args,
2147 std::enable_if_t<std::is_constructible_v<E, std::initializer_list<U>&,
2148 Args...>>* = nullptr>
2149 constexpr explicit expected(
2150 unexpect_t, std::initializer_list<U> il,
2151 Args&&... args) noexcept(std::is_nothrow_constructible_v<E,
2152 std::initializer_list<U>&,
2153 Args...>)
2154 : impl_base(unexpect, il, std::forward<Args>(args)...),
2156
2157 // helper ctor for transform_error()
2158 template <class Fn, class... Args>
2159 constexpr explicit expected(
2161 Args&&... args) noexcept(noexcept(static_cast<E>(std::invoke(std::forward<Fn>(func),
2162 std::forward<Args>(
2163 args)...))))
2164 : impl_base(tag1, tag2, std::forward<Fn>(func), std::forward<Args>(args)...),
2166
2167 expected& operator=(const expected& rhs) = default;
2168 expected& operator=(expected&& rhs) = default;
2169
2170 template <class G, class GF = const G&,
2171 std::enable_if_t<std::is_constructible_v<E, GF> &&
2172 std::is_assignable_v<E&, GF>>* = nullptr>
2173 constexpr expected& operator=(const unexpected<G>& rhs) noexcept(
2174 std::is_nothrow_constructible_v<E, GF> && std::is_nothrow_assignable_v<E&, GF>) {
2175 if (has_value()) {
2176 expected_detail::construct_at(errptr(), std::forward<GF>(rhs.error()));
2177 this->m_has_val = false;
2178 } else {
2179 error() = std::forward<GF>(rhs.error());
2180 }
2181
2182 return *this;
2183 }
2184
2185 template <class G, class GF = G,
2186 std::enable_if_t<std::is_constructible_v<E, GF> &&
2187 std::is_assignable_v<E&, GF>>* = nullptr>
2188 constexpr expected& operator=(unexpected<G>&& rhs) noexcept(
2189 std::is_nothrow_constructible_v<E, GF> && std::is_nothrow_assignable_v<E&, GF>) {
2190 if (has_value()) {
2191 expected_detail::construct_at(errptr(), std::forward<GF>(rhs.error()));
2192 this->m_has_val = false;
2193 } else {
2194 error() = std::forward<GF>(rhs.error());
2195 }
2196
2197 return *this;
2198 }
2199
2200 constexpr void emplace() {
2201 if (!has_value()) {
2202 if constexpr (!std::is_trivially_destructible_v<E>) {
2203 err().~E();
2204 }
2205 this->m_has_val = true;
2206 }
2207 }
2208
2209 template <class OE = E>
2210 constexpr std::enable_if_t<std::is_swappable_v<OE> && std::is_move_constructible_v<OE>>
2211 swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v<E> &&
2212 std::is_nothrow_swappable_v<E>) {
2213 using std::swap;
2214 if (this->m_has_val && rhs.m_has_val) {
2215 // do nothing
2216 } else if (this->m_has_val) {
2217 expected_detail::construct_at(std::addressof(error()), std::move(rhs.error()));
2218 if constexpr (!std::is_trivially_destructible_v<E>) {
2219 rhs.error().~E();
2220 }
2221 this->m_has_val = false;
2222 rhs.m_has_val = true;
2223 } else if (rhs.m_has_val) {
2224 expected_detail::construct_at(std::addressof(rhs.error()), std::move(error()));
2225 if constexpr (!std::is_trivially_destructible_v<E>) {
2226 error().~E();
2227 }
2228 this->m_has_val = true;
2229 rhs.m_has_val = false;
2230 } else {
2231 swap(error(), rhs.error()); // ADL
2232 }
2233 }
2234
2235 constexpr void operator*() const noexcept { return val(); }
2236
2237 constexpr bool has_value() const noexcept { return this->m_has_val; }
2238 constexpr explicit operator bool() const noexcept { return this->m_has_val; }
2239
2240 constexpr void value() const& {
2241 static_assert(std::is_copy_constructible_v<E>,
2242 "E must be copy constructible, by LWG-3940");
2243 if (!has_value()) ICEBERG_EXPECTED_THROW(bad_expected_access(err()));
2244 }
2245
2246 constexpr void value() && {
2247 static_assert(std::is_copy_constructible_v<E>,
2248 "E must be copy constructible, by LWG-3940");
2249 static_assert(std::is_move_constructible_v<E>,
2250 "E must be move constructible, by LWG-3940");
2251 if (!has_value()) ICEBERG_EXPECTED_THROW(bad_expected_access(std::move(err())));
2252 }
2253
2254 constexpr const E& error() const& noexcept { return err(); }
2255 constexpr E& error() & noexcept { return err(); }
2256 constexpr const E&& error() const&& noexcept { return std::move(err()); }
2257 constexpr E&& error() && noexcept { return std::move(err()); }
2258
2259 template <class G = E>
2260 constexpr E error_or(G&& v) const& noexcept(
2261 std::is_nothrow_copy_constructible_v<E> &&
2262 expected_detail::is_nothrow_convertible_v<G, E>) {
2263 static_assert(std::is_copy_constructible_v<E>, "E must be copy-constructible");
2264 static_assert(std::is_convertible_v<G, E>, "is_convertible_v<G, E> must be true");
2265 if (this->m_has_val) {
2266 return static_cast<E>(std::forward<G>(v));
2267 } else {
2268 return this->m_unexpect;
2269 }
2270 }
2271 template <class G = E>
2272 constexpr E error_or(G&& v) && noexcept(
2273 std::is_nothrow_move_constructible_v<E> &&
2274 expected_detail::is_nothrow_convertible_v<G, E>) {
2275 static_assert(std::is_move_constructible_v<E>, "E must be move-constructible");
2276 static_assert(std::is_convertible_v<G, E>, "is_convertible_v<G, E> must be true");
2277 if (this->m_has_val) {
2278 return static_cast<E>(std::forward<G>(v));
2279 } else {
2280 return std::move(this->m_unexpect);
2281 }
2282 }
2283
2284 template <class F, class GE = E,
2285 std::enable_if_t<std::is_constructible_v<GE, GE&>>* = nullptr>
2286 constexpr auto and_then(F&& f) & {
2287 using U = expected_detail::remove_cvref_t<std::invoke_result_t<F>>;
2288 static_assert(expected_detail::is_specialization_v<U, iceberg::expected>,
2289 "U (return type of F) must be specialization of expected");
2290 static_assert(std::is_same_v<typename U::error_type, E>,
2291 "The error type must be the same after calling the F");
2292
2293 if (has_value())
2294 return std::invoke(std::forward<F>(f));
2295 else
2296 return U(unexpect, error());
2297 }
2298 template <class F, class GE = E,
2299 std::enable_if_t<std::is_constructible_v<GE, const GE&>>* = nullptr>
2300 constexpr auto and_then(F&& f) const& {
2301 using U = expected_detail::remove_cvref_t<std::invoke_result_t<F>>;
2302 static_assert(expected_detail::is_specialization_v<U, iceberg::expected>,
2303 "U (return type of F) must be specialization of expected");
2304 static_assert(std::is_same_v<typename U::error_type, E>,
2305 "The error type must be the same after calling the F");
2306
2307 if (has_value())
2308 return std::invoke(std::forward<F>(f));
2309 else
2310 return U(unexpect, error());
2311 }
2312 template <class F, class GE = E,
2313 std::enable_if_t<std::is_constructible_v<GE, GE&&>>* = nullptr>
2314 constexpr auto and_then(F&& f) && {
2315 using U = expected_detail::remove_cvref_t<std::invoke_result_t<F>>;
2316 static_assert(expected_detail::is_specialization_v<U, iceberg::expected>,
2317 "U (return type of F) must be specialization of expected");
2318 static_assert(std::is_same_v<typename U::error_type, E>,
2319 "The error type must be the same after calling the F");
2320
2321 if (has_value())
2322 return std::invoke(std::forward<F>(f));
2323 else
2324 return U(unexpect, std::move(error()));
2325 }
2326 template <class F, class GE = E,
2327 std::enable_if_t<std::is_constructible_v<GE, const GE>>* = nullptr>
2328 constexpr auto and_then(F&& f) const&& {
2329 using U = expected_detail::remove_cvref_t<std::invoke_result_t<F>>;
2330 static_assert(expected_detail::is_specialization_v<U, iceberg::expected>,
2331 "U (return type of F) must be specialization of expected");
2332 static_assert(std::is_same_v<typename U::error_type, E>,
2333 "The error type must be the same after calling the F");
2334
2335 if (has_value())
2336 return std::invoke(std::forward<F>(f));
2337 else
2338 return U(unexpect, std::move(error()));
2339 }
2340
2341 template <class F>
2342 constexpr auto or_else(F&& f) & {
2343 using G = expected_detail::remove_cvref_t<std::invoke_result_t<F, decltype(error())>>;
2344 static_assert(expected_detail::is_specialization_v<G, iceberg::expected>,
2345 "G (return type of F) must be specialization of expected");
2346 static_assert(std::is_same_v<typename G::value_type, T>,
2347 "The value type must be the same after calling the F");
2348
2349 if (has_value())
2350 return G();
2351 else
2352 return std::invoke(std::forward<F>(f), error());
2353 }
2354 template <class F>
2355 constexpr auto or_else(F&& f) const& {
2356 using G = expected_detail::remove_cvref_t<std::invoke_result_t<F, decltype(error())>>;
2357 static_assert(expected_detail::is_specialization_v<G, iceberg::expected>,
2358 "G (return type of F) must be specialization of expected");
2359 static_assert(std::is_same_v<typename G::value_type, T>,
2360 "The value type must be the same after calling the F");
2361
2362 if (has_value())
2363 return G();
2364 else
2365 return std::invoke(std::forward<F>(f), error());
2366 }
2367 template <class F>
2368 constexpr auto or_else(F&& f) && {
2369 using G = expected_detail::remove_cvref_t<
2370 std::invoke_result_t<F, decltype(std::move(error()))>>;
2371 static_assert(expected_detail::is_specialization_v<G, iceberg::expected>,
2372 "G (return type of F) must be specialization of expected");
2373 static_assert(std::is_same_v<typename G::value_type, T>,
2374 "The value type must be the same after calling the F");
2375
2376 if (has_value())
2377 return G();
2378 else
2379 return std::invoke(std::forward<F>(f), std::move(error()));
2380 }
2381 template <class F>
2382 constexpr auto or_else(F&& f) const&& {
2383 using G = expected_detail::remove_cvref_t<
2384 std::invoke_result_t<F, decltype(std::move(error()))>>;
2385 static_assert(expected_detail::is_specialization_v<G, iceberg::expected>,
2386 "G (return type of F) must be specialization of expected");
2387 static_assert(std::is_same_v<typename G::value_type, T>,
2388 "The value type must be the same after calling the F");
2389
2390 if (has_value())
2391 return G();
2392 else
2393 return std::invoke(std::forward<F>(f), std::move(error()));
2394 }
2395
2396 template <class F, class GE = E,
2397 std::enable_if_t<std::is_constructible_v<GE, GE&>>* = nullptr>
2398 constexpr auto transform(F&& f) & {
2399 using U = expected_detail::remove_cvref_t<std::invoke_result_t<F>>;
2400 static_assert(expected_detail::is_value_type_valid_v<U>,
2401 "U must be a valid type for expected<U, E>");
2402 // FIXME another constraint needed here
2403 if (!has_value()) {
2404 return expected<U, E>(unexpect, error());
2405 } else {
2406 if constexpr (std::is_void_v<U>) {
2407 std::invoke(std::forward<F>(f));
2408 return expected<U, E>{};
2409 } else {
2411 std::forward<F>(f));
2412 }
2413 }
2414 }
2415 template <class F, class GE = E,
2416 std::enable_if_t<std::is_constructible_v<GE, const GE&>>* = nullptr>
2417 constexpr auto transform(F&& f) const& {
2418 using U = expected_detail::remove_cvref_t<std::invoke_result_t<F>>;
2419 static_assert(expected_detail::is_value_type_valid_v<U>,
2420 "U must be a valid type for expected<U, E>");
2421 // FIXME another constraint needed here
2422 if (!has_value()) {
2423 return expected<U, E>(unexpect, error());
2424 } else {
2425 if constexpr (std::is_void_v<U>) {
2426 std::invoke(std::forward<F>(f));
2427 return expected<U, E>{};
2428 } else {
2430 std::forward<F>(f));
2431 }
2432 }
2433 }
2434 template <class F, class GE = E,
2435 std::enable_if_t<std::is_constructible_v<GE, GE&&>>* = nullptr>
2436 constexpr auto transform(F&& f) && {
2437 using U = expected_detail::remove_cvref_t<std::invoke_result_t<F>>;
2438 static_assert(expected_detail::is_value_type_valid_v<U>,
2439 "U must be a valid type for expected<U, E>");
2440 // FIXME another constraint needed here
2441 if (!has_value()) {
2442 return expected<U, E>(unexpect, std::move(error()));
2443 } else {
2444 if constexpr (std::is_void_v<U>) {
2445 std::invoke(std::forward<F>(f));
2446 return expected<U, E>{};
2447 } else {
2449 std::forward<F>(f));
2450 }
2451 }
2452 }
2453 template <class F, class GE = E,
2454 std::enable_if_t<std::is_constructible_v<GE, const GE>>* = nullptr>
2455 constexpr auto transform(F&& f) const&& {
2456 using U = expected_detail::remove_cvref_t<std::invoke_result_t<F>>;
2457 static_assert(expected_detail::is_value_type_valid_v<U>,
2458 "U must be a valid type for expected<U, E>");
2459 // FIXME another constraint needed here
2460 if (!has_value()) {
2461 return expected<U, E>(unexpect, std::move(error()));
2462 } else {
2463 if constexpr (std::is_void_v<U>) {
2464 std::invoke(std::forward<F>(f));
2465 return expected<U, E>{};
2466 } else {
2468 std::forward<F>(f));
2469 }
2470 }
2471 }
2472
2473 template <class F>
2474 constexpr auto transform_error(F&& f) & {
2475 using G = expected_detail::remove_cvref_t<std::invoke_result_t<F, decltype(error())>>;
2476 static_assert(expected_detail::is_error_type_valid_v<G>,
2477 "G must be a valid type for expected<void, G>");
2478 // FIXME another constraint needed here
2479 if (has_value()) {
2480 return expected<T, G>();
2481 } else {
2483 std::forward<F>(f), error());
2484 }
2485 }
2486 template <class F>
2487 constexpr auto transform_error(F&& f) const& {
2488 using G = expected_detail::remove_cvref_t<std::invoke_result_t<F, decltype(error())>>;
2489 static_assert(expected_detail::is_error_type_valid_v<G>,
2490 "G must be a valid type for expected<void, G>");
2491 // FIXME another constraint needed here
2492 if (has_value()) {
2493 return expected<T, G>();
2494 } else {
2496 std::forward<F>(f), error());
2497 }
2498 }
2499 template <class F>
2500 constexpr auto transform_error(F&& f) && {
2501 using G = expected_detail::remove_cvref_t<
2502 std::invoke_result_t<F, decltype(std::move(error()))>>;
2503 static_assert(expected_detail::is_error_type_valid_v<G>,
2504 "G must be a valid type for expected<void, G>");
2505 // FIXME another constraint needed here
2506 if (has_value()) {
2507 return expected<T, G>();
2508 } else {
2510 std::forward<F>(f), std::move(error()));
2511 }
2512 }
2513 template <class F>
2514 constexpr auto transform_error(F&& f) const&& {
2515 using G = expected_detail::remove_cvref_t<
2516 std::invoke_result_t<F, decltype(std::move(error()))>>;
2517 static_assert(expected_detail::is_error_type_valid_v<G>,
2518 "G must be a valid type for expected<void, G>");
2519 // FIXME another constraint needed here
2520 if (has_value()) {
2521 return expected<T, G>();
2522 } else {
2524 std::forward<F>(f), std::move(error()));
2525 }
2526 }
2527
2528 template <class T2, class E2>
2529 [[nodiscard]] friend constexpr std::enable_if_t<
2530 std::is_void_v<T2> &&
2531 expected_detail::is_equality_result_convertible_to_bool_v<E, E2>,
2532 bool>
2533 operator==(const expected& x, const expected<T2, E2>& y) noexcept(
2534 expected_detail::is_nothrow_equality_result_convertible_to_bool_v<E, E2>) {
2535 if (x.has_value() != y.has_value()) {
2536 return false;
2537 } else if (x.has_value()) {
2538 return true;
2539 } else {
2540 return x.error() == y.error();
2541 }
2542 }
2543#if ICEBERG_EXPECTED_CPLUSPLUS < 202002L
2544 template <class T2, class E2>
2545 [[nodiscard]] friend constexpr std::enable_if_t<
2546 std::is_void_v<T2> &&
2547 expected_detail::is_equality_result_convertible_to_bool_v<E, E2>,
2548 bool> operator!=(const expected& x,
2549 const expected<T2, E2>& y) noexcept(noexcept(x == y)) {
2550 return !(x == y);
2551 }
2552#endif
2553
2554 template <class E2>
2555 [[nodiscard]] friend constexpr std::enable_if_t<
2556 expected_detail::is_equality_result_convertible_to_bool_v<E, E2>, bool>
2557 operator==(const expected& x, const unexpected<E2>& e) noexcept(
2558 expected_detail::is_nothrow_equality_result_convertible_to_bool_v<E, E2>) {
2559 if (x.has_value()) {
2560 return false;
2561 } else {
2562 return x.error() == e.error();
2563 }
2564 }
2565#if ICEBERG_EXPECTED_CPLUSPLUS < 202002L
2566 template <class E2>
2567 [[nodiscard]] friend constexpr std::enable_if_t<
2568 expected_detail::is_equality_result_convertible_to_bool_v<E, E2>, bool>
2569 operator!=(const expected& x, const unexpected<E2>& e) noexcept(noexcept(x == e)) {
2570 return !(x == e);
2571 }
2572#endif
2573};
2574
2575#if ICEBERG_EXPECTED_CPLUSPLUS < 202002L
2576// Deduce the expected operand to reject conversions exposed by MSVC's permissive C++17
2577// hidden-friend lookup.
2578template <class T, class E, class T2>
2579[[nodiscard]] constexpr std::enable_if_t<
2580 !std::is_void_v<T> && !expected_detail::is_specialization_v<T2, iceberg::expected> &&
2581 expected_detail::is_equality_result_convertible_to_bool_v<T, T2>,
2582 bool>
2583operator==(const T2& v, const expected<T, E>& x) noexcept(noexcept(x == v)) {
2584 return x == v;
2585}
2586
2587template <class T, class E, class T2>
2588[[nodiscard]] constexpr std::enable_if_t<
2589 !std::is_void_v<T> && !expected_detail::is_specialization_v<T2, iceberg::expected> &&
2590 expected_detail::is_equality_result_convertible_to_bool_v<T, T2>,
2591 bool>
2592operator!=(const T2& v, const expected<T, E>& x) noexcept(noexcept(x == v)) {
2593 return x != v;
2594}
2595
2596template <
2597 class T, class E, class E2,
2598 std::enable_if_t<expected_detail::is_equality_result_convertible_to_bool_v<E, E2>>* =
2599 nullptr>
2600[[nodiscard]] constexpr bool operator==(const unexpected<E2>& e,
2601 const expected<T, E>& x) noexcept(noexcept(x ==
2602 e)) {
2603 return x == e;
2604}
2605
2606template <
2607 class T, class E, class E2,
2608 std::enable_if_t<expected_detail::is_equality_result_convertible_to_bool_v<E, E2>>* =
2609 nullptr>
2610[[nodiscard]] constexpr bool operator!=(const unexpected<E2>& e,
2611 const expected<T, E>& x) noexcept(noexcept(x ==
2612 e)) {
2613 return x != e;
2614}
2615#endif
2616
2617// standalone swap for void value type
2618template <class E, std::enable_if_t<std::is_move_constructible_v<E> &&
2619 std::is_swappable_v<E>>* = nullptr>
2620constexpr void swap(expected<void, E>& lhs,
2621 expected<void, E>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
2622 lhs.swap(rhs);
2623}
2624
2625} // namespace iceberg
2626
2627// NOLINTEND
Definition expected.h:344
Definition expected.h:1199
Definition expected.h:246
Define symbol visibility macros for core Iceberg APIs.
Core Apache Iceberg C++ APIs.
Definition arrow_io_util.h:34
STL namespace.
Definition expected.h:475
Definition expected.h:318