sumty  0.1.0
Better sum types for C++
error_set.hpp
1 /* Copyright 2024 Jack A Bernard Jr.
2  *
3  * Licensed under the Apache License, Version 2.0 (the License);
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an AS IS BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef SUMTY_ERROR_SET_HPP
17 #define SUMTY_ERROR_SET_HPP
18 
19 #include "sumty/detail/fwd.hpp" // IWYU pragma: export
20 #include "sumty/detail/traits.hpp" // IWYU pragma: export
21 #include "sumty/detail/utils.hpp"
22 #include "sumty/utils.hpp"
23 #include "sumty/variant.hpp"
24 
25 #include <cstddef>
26 #include <initializer_list>
27 #include <type_traits>
28 #include <utility>
29 
30 namespace sumty {
31 
32 /// @class error_set error_set.hpp <sumty/error_set.hpp>
33 /// @brief A discriminated union of unique error types
34 ///
35 /// @details
36 /// @ref error_set is very similar to @ref variant in term of API. The key
37 /// difference from @ref variant is that @ref error_set requires all its
38 /// alternatives to be unique, and @ref error_set allows direct conversions
39 /// between different, but compatible, @ref error_set instantiations.
40 ///
41 /// Given distinct types `A`, `B`, and `C`, @ref error_set allows the
42 /// following implicit conversions (this is not exhaustive):
43 /// * `A -> error_set<A, B, C>`
44 /// * `B -> error_set<A, B, C>`
45 /// * `C -> error_set<A, B, C>`
46 /// * `error_set<B> -> error_set<A, B, C>`
47 /// * `error_set<C, A> -> error_set<A, B, C>`
48 /// * `error_set<C, A, B> -> error_set<A, B, C>`
49 ///
50 /// Generally, an @ref error_set can convert into a different error set as long
51 /// as each of its alternatives unambiguously map to an alternative in the
52 /// destination @ref error_set. Additionally, any bare type can convert into an
53 /// @ref error_set if it can unambiguously convert into one of the destination
54 /// alternatives.
55 ///
56 /// The primary purpose of @ref error_set is to improve the convenience of
57 /// error propagation with @ref result. The conversions shown above also apply
58 /// to an @ref error_set nested as the error type of a @ref result. Below is a
59 /// listing of the previous conversions examples, but now wrapped in @ref
60 /// result, which is equally valid.
61 /// * `result<T, A> -> result<T, error_set<A, B, C>>`
62 /// * `result<T, B> -> result<T, error_set<A, B, C>>`
63 /// * `result<T, C> -> result<T, error_set<A, B, C>>`
64 /// * `result<T, error_set<B>> -> result<T, error_set<A, B, C>>`
65 /// * `result<T, error_set<C, A>> -> result<T, error_set<A, B, C>>`
66 /// * `result<T, error_set<C, A, B>> -> result<T, error_set<A, B, C>>`
67 ///
68 /// Below is an example of how @ref error_set might be used in practice.
69 /// ```
70 /// // specific error types
71 /// struct neg_int_error {};
72 /// struct int_parse_error {};
73 /// struct int_overflow_error {};
74 ///
75 /// // union of the above error types
76 /// using my_errors = error_set<
77 /// neg_int_error,
78 /// int_parse_error,
79 /// int_overflow_error
80 /// >;
81 ///
82 /// // parse an integer from a string
83 /// result<int, int_parse_error> parse_int(std::string_view str);
84 ///
85 /// // calculate the absolute value of an integer
86 /// result<int, int_overflow_error> abs_value(int x);
87 ///
88 /// // calculate the integer square root
89 /// result<int, neg_int_error> isqrt(int x);
90 ///
91 /// result<int, my_errors> my_algorithm(std::string_view str) {
92 /// auto parsed = parse_int(str);
93 /// if (parsed.is_error()) { return parsed; }
94 ///
95 /// auto positive = abs_value(*parsed);
96 /// if (positive.is_error()) { return positive; }
97 ///
98 /// return isqrt(*positive);
99 /// }
100 /// ```
101 ///
102 /// One possible pattern when using @ref error_set is to define all errors as
103 /// empty types. Like all other sum types in sumty, an @ref error_set of all
104 /// empty types only needs to actually store the discriminant. In this case,
105 /// @ref error_set behaves like a flexible enum partially mapped into the type
106 /// system. That is, a specific (empty) error type uniquely identifies an
107 /// alternative in any @ref error_set that contains that error type, similar to
108 /// how an enum value name identifies a value of that enum. So it is possible
109 /// to create a set of error codes as types without giving them specific values
110 /// and define subsets of those error codes for different use cases. A function
111 /// might emit some, but not all, of the errors in your set of error codes, so
112 /// that function need only declare that it can return those specific errors
113 /// instead of all codes in the set.
114 ///
115 /// That said, it is also perfectly valid for each error type to contain
116 /// additional error information as needed.
117 template <typename... T>
118 class error_set : variant<T...> {
119  private:
120  static_assert(detail::all_unique_v<T...>,
121  "All error types in an error_set must be unqiue");
122 
123  [[nodiscard]] constexpr variant<T...>& set_() & noexcept {
124  return *static_cast<variant<T...>*>(this);
125  }
126 
127  [[nodiscard]] constexpr const variant<T...>& set_() const& noexcept {
128  return *static_cast<const variant<T...>*>(this);
129  }
130 
131  [[nodiscard]] constexpr variant<T...>&& set_() && {
132  return std::move(*static_cast<variant<T...>*>(this));
133  }
134 
135  [[nodiscard]] constexpr const variant<T...>&& set_() const&& {
136  return std::move(*static_cast<const variant<T...>*>(this));
137  }
138 
139  template <typename...>
140  friend class error_set;
141 
142  public:
143  /// @brief Default constructor
144  ///
145  /// @details
146  /// Initializes the @ref error_set such that it contains a default
147  /// constructed value of the first (index 0) alternative.
148  ///
149  /// The first alternative *must* be default constructible for this
150  /// constructor to participate in overload resoltuion, but no other
151  /// alternatives need be default constructible.
152  ///
153  /// ## Example
154  /// ```cpp
155  /// error_set<error1, error2> set;
156  ///
157  /// assert(holds_alternative<error1>(set));
158  ///
159  /// assert(set.index() == 0);
160  /// ```
161  constexpr error_set()
162 #ifndef DOXYGEN
164  requires(std::is_default_constructible_v<variant<T...>>)
165  = default;
166 #else
167  CONDITIONALLY_NOEXCEPT;
168 #endif
169 
170  /// @brief Copy constructor
171  ///
172  /// @details
173  /// A new @ref error_set is initialized such that it contains a copy
174  /// constructed instance of the alternative contained in the source @ref
175  /// error_set. If the source @ref error_set has more than one alternative of
176  /// the same type, the new @ref error_set will contain the alternative of the
177  /// same index.
178  ///
179  /// All alternative types *must* be copy constructible for this constructor
180  /// to participate in overload resolution.
181  ///
182  /// ## Example
183  /// ```cpp
184  /// error_set<error1, error2> e1{std::in_place_index<1>};
185  ///
186  /// error_set<error1, error2> e2{e1};
187  ///
188  /// assert(holds_alternative<error2>(e2));
189  ///
190  /// assert(e2.index() == 1);
191  /// ```
192  constexpr error_set(const error_set&)
193 #ifndef DOXYGEN
195  requires(std::is_copy_constructible_v<variant<T...>>)
196  = default;
197 #else
198  CONDITIONALLY_NOEXCEPT;
199 #endif
200 
201  /// @brief Move constructor
202  ///
203  /// @details
204  /// A new @ref error_set is initialized such that it contains a move
205  /// constructed instance of the alternative contained in the source @ref
206  /// error_set. If the source @ref error_set has more than one alternative of
207  /// the same type, the new @ref error_set will contain the alternative of the
208  /// same index.
209  ///
210  /// All alternative types *must* be move constructible for this constructor
211  /// to participate in overload resolution.
212  ///
213  /// The source @ref error_set will continue to contain an instance of the same
214  /// alternative, but the value of the alternative after being moved depends
215  /// on the move constructor of the type. In general, moved values are said
216  /// to be in a valid, but unspecified, state.
217  ///
218  /// ## Example
219  /// ```cpp
220  /// error_set<error1, error2> e1{std::in_place_index<1>};
221  ///
222  /// error_set<error1, error2> e2{std::move(e1)};
223  ///
224  /// assert(holds_alternative<error2>(e2));
225  ///
226  /// assert(e2.index() == 1);
227  /// ```
228  constexpr error_set(error_set&&)
229 #ifndef DOXYGEN
231  requires(std::is_move_constructible_v<variant<T...>>)
232  = default;
233 #else
234  CONDITIONALLY_NOEXCEPT;
235 #endif
236 
237  /// @brief Index emplacement constructor
238  ///
239  /// @details
240  /// A new @ref error_set is initialized such that it contains a newly
241  /// constructed instance of the alternative with the specified index.
242  /// The arguments following `inplace` are forwarded directly to the
243  /// constructor of the alternative type.
244  ///
245  /// Given that `U` is the type of the alternative at the specified index,
246  /// this constructor is always valid as long as `U` is constructible with
247  /// the arguments, `std::forward<Args>(args)...`.
248  ///
249  /// ## Example
250  /// ```cpp
251  /// error_set<error, std::string> e{std::in_place_index<1>, 5, 'a'};
252  ///
253  /// assert(holds_alternative<std::string>(e));
254  ///
255  /// assert(e.index() == 1);
256  ///
257  /// assert(get<1>(e) == "aaaaa");
258  /// ```
259  ///
260  /// @param inplace Constructor tag that specifies the alternative index.
261  /// @param args Arguments used to construct the alternative.
262  template <size_t IDX, typename... Args>
263 #ifndef DOXYGEN
264  explicit(sizeof...(Args) == 0)
265 #else
267 #endif
268  // NOLINTNEXTLINE(hicpp-explicit-conversions)
269  constexpr error_set(std::in_place_index_t<IDX> inplace, Args&&... args)
270  : variant<T...>(inplace, std::forward<Args>(args)...) {
271  }
272 
273  /// @brief Index emplacement constructor with `std::initializer_list`
274  ///
275  /// @details
276  /// A new @ref error_set is initialized such that it contains a newly
277  /// constructed instance of the alternative with the specified index.
278  /// The arguments following `inplace` are forwarded directly to the
279  /// constructor of the alternative type.
280  ///
281  /// Given that `U` is the type of the alternative at the specified index,
282  /// this constructor is always valid as long as `U` is constructible with
283  /// the arguments, `init, std::forward<Args>(args)...`.
284  ///
285  /// ## Example
286  /// ```cpp
287  /// error_set<error, std::vector<int>> e{
288  /// std::in_place_index<1>,
289  /// {1, 2, 3, 4, 5}};
290  ///
291  /// assert(holds_alternative<std::vector<int>>(e));
292  ///
293  /// assert(e.index() == 1);
294  ///
295  /// assert(get<1>(e).size() == 5);
296  /// ```
297  ///
298  /// @param inplace Constructor tag that specifies the alternative index.
299  /// @param init Initializer list forwarded to the alternative constructor
300  /// @param args Additional arguments used to construct the alternative.
301  template <size_t IDX, typename U, typename... Args>
302  constexpr error_set(std::in_place_index_t<IDX> inplace,
303  std::initializer_list<U> init,
304  Args&&... args)
305  : variant<T...>(inplace, init, std::forward<Args>(args)...) {}
306 
307  /// @brief Type emplacement constructor
308  ///
309  /// @details
310  /// A new @ref error_set is initialized such that it contains a newly
311  /// constructed instance of the alternative with the specified type.
312  /// The arguments following `inplace` are forwarded directory to the
313  /// constructor of the alternative type.
314  ///
315  /// ## Example
316  /// ```cpp
317  /// error_set<error, std::string> e{
318  /// std::in_place_type<std::string>,
319  /// 5, 'a'};
320  ///
321  /// assert(holds_alternative<std::string>(e));
322  ///
323  /// assert(e.index() == 1);
324  ///
325  /// assert(get<1>(e) == "aaaaa");
326  /// ```
327  template <typename U, typename... Args>
328 #ifndef DOXYGEN
329  explicit(sizeof...(Args) == 0)
330 #else
332 #endif
333  // NOLINTNEXTLINE(hicpp-explicit-conversions)
334  constexpr error_set([[maybe_unused]] std::in_place_type_t<U> inplace,
335  Args&&... args)
336  : variant<T...>(inplace, std::forward<Args>(args)...) {
337  }
338 
339  /// @brief Type emplacement constructor with `std::initializer_list`
340  ///
341  /// @details
342  /// A new @ref error_set is initialized such that it contains a newly
343  /// constructed instance of the alternative with the specified type.
344  /// The arguments following `inplace` are forwarded directory to the
345  /// constructor of the alternative type.
346  ///
347  /// ## Example
348  /// ```cpp
349  /// error_set<error, std::vector<int>> e{
350  /// std::in_place_type<std::vector<int>>,
351  /// {1, 2, 3, 4, 5}};
352  ///
353  /// assert(holds_alternative<std::vector<int>>(e));
354  ///
355  /// assert(e.index() == 1);
356  ///
357  /// assert(get<1>(e).size() == 5);
358  /// ```
359  template <typename U, typename V, typename... Args>
360  constexpr error_set([[maybe_unused]] std::in_place_type_t<U> inplace,
361  std::initializer_list<V> init,
362  Args&&... args)
363  : variant<T...>(inplace, init, std::forward<Args>(args)...) {}
364 
365  /// @brief Forwarding constructor
366  ///
367  /// @details
368  /// A new @ref error_set is initialized such that an alternative is
369  /// constructed in place with the provided value as the constructor
370  /// argument.
371  ///
372  /// To avoid ambiguity to the reader, this constructor only participates
373  /// in overload resolution when there is only one alternative that could
374  /// possibly be constructed from the value.
375  ///
376  /// This constructor is `explicit` if the value is not implicitly
377  /// convertible to the target alternative type.
378  ///
379  /// ## Example
380  /// ```cpp
381  /// error_set<error, std::string> e{"oh no"};
382  ///
383  /// assert(e.index() == 1);
384  ///
385  /// assert(get<1>(e) == "oh no");
386  /// ```
387  ///
388  /// @param value The value that is used to construct the alternative
389  template <typename U>
390 #ifndef DOXYGEN
391  requires(!detail::is_error_set_v<std::remove_cvref_t<U>> &&
394 #else
396 #endif
397  // NOLINTNEXTLINE(hicpp-explicit-conversions)
398  constexpr error_set(U&& value)
399  : variant<T...>(std::forward<U>(value)) {
400  }
401 
402  /// @brief Forwarding constructor with initializer list
403  ///
404  /// @details
405  /// A new @ref error_set is initialized such that an alternative is
406  /// constructed in place with the provided `std::initializer_list`
407  /// as the constructor argument.
408  ///
409  /// To avoid ambiguity to the reader, this constructor only participates
410  /// in overload resulotion when there is only one alternative that could
411  /// possible be constructed from the initializer list.
412  ///
413  /// This constructor is `explicit` if the `std::initializer_list` is not
414  /// implicitly convertible to the target alternative type.
415  ///
416  /// ## Example
417  /// ```cpp
418  /// error_set<error, std::vector<int>> e({1, 2, 3, 4, 5});
419  ///
420  /// assert(e.index() == 1);
421  ///
422  /// assert(get<1>(e).size() == 5);
423  /// ```
424  ///
425  /// @param init The `std::initializer_list` that is used to construct the
426  /// alternative
427  template <typename U>
428 #ifndef DOXYGEN
431 #else
433 #endif
434  constexpr error_set(std::initializer_list<U> init)
435  : variant<T...>(init) {
436  }
437 
438  /// @brief Copy conversion constructor
439  ///
440  /// @details
441  /// A new @ref error_set is initialized from the value contained in another
442  /// @ref error_set that is a non-strict subset of the destination @ref
443  /// error_set. Each of the alternative types in the source @ref error_set
444  /// must also be an alternative type in the destination @ref error_set, but
445  /// they need not have the same index.
446  ///
447  /// This constructor allows for values of @ref error_set to be easily
448  /// propagated, whether returned directly, or as an alternative of any
449  /// other sum type. Most commonly, this will be used to implicitly convert
450  /// from a @ref result to another result with a superset of the errors as
451  /// the source, such as when returning from a function.
452  ///
453  /// ## Example
454  /// ```
455  /// const error_set<error1, error2> e1{std::in_place_index<1>};
456  ///
457  /// const error_set<error2, error3, error1> e2{e1};
458  ///
459  /// assert(holds_alternative<error2>(e2));
460  ///
461  /// assert(e2.index() == 0);
462  /// ```
463  template <typename... U>
464 #ifndef DOXYGEN
465  requires(!std::is_same_v<error_set<U...>, error_set<T...>> &&
467 #endif
468  // NOLINTNEXTLINE(hicpp-explicit-conversions)
469  constexpr error_set(const error_set<U...>& other) : variant<T...>(detail::uninit) {
470  other.set_().visit_informed([this](const auto& value, auto info) {
471  set_()
472  .template uninit_emplace<
473  detail::index_of_v<typename decltype(info)::type, T...>>(value);
474  });
475  }
476 
477  /// @brief Move conversion constructor
478  ///
479  /// @details
480  /// A new @ref error_set is initialized from the value contained in another
481  /// @ref error_set that is a non-strict subset of the destination @ref
482  /// error_set. Each of the alternative types in the source @ref error_set
483  /// must also be an alternative type in the destination @ref error_set, but
484  /// they need not have the same index.
485  ///
486  /// This constructor allows for values of @ref error_set to be easily
487  /// propagated, whether returned directly, or as an alternative of any
488  /// other sum type. Most commonly, this will be used to implicitly convert
489  /// from a @ref result to another result with a superset of the errors as
490  /// the source, such as when returning from a function.
491  ///
492  /// ## Example
493  /// ```
494  /// error_set<error1, error2> e1{std::in_place_index<1>};
495  ///
496  /// error_set<error2, error3, error1> e2{std::move(e1)};
497  ///
498  /// assert(holds_alternative<error2>(e2));
499  ///
500  /// assert(e2.index() == 0);
501  /// ```
502  template <typename... U>
503 #ifndef DOXYGEN
504  requires(!std::is_same_v<error_set<U...>, error_set<T...>> &&
506 #endif
507  // clang-format off
508  // NOLINTNEXTLINE(hicpp-explicit-conversions,cppcoreguidelines-rvalue-reference-param-not-moved)
509  constexpr error_set(error_set<U...>&& other) : variant<T...>(detail::uninit) {
510  // clang-format on
511  std::move(other.set_()).visit_informed([this](auto&& value, auto info) {
512  set_()
513  .template uninit_emplace<
514  detail::index_of_v<typename decltype(info)::type, T...>>(
515  info.forward(value));
516  });
517  }
518 
519  /// @brief Destructor
520  ///
521  /// @details
522  /// The contained alternative of the @ref error_set will is destroyed in
523  /// place.
524  ///
525  /// The destructor is `noexcept` if all alternative types are nothrow
526  /// destructible.
527  constexpr ~error_set()
528 #ifndef DOXYGEN
529  noexcept(std::is_nothrow_destructible_v<variant<T...>>) = default;
530 #else
531  CONDITIONALLY_NOEXCEPT;
532 #endif
533 
534  /// @brief Copy assignment operator
535  ///
536  /// @details
537  /// Copy assignment of an @ref error_set can take one of two possible code
538  /// paths.
539  ///
540  /// If the source and destination @ref error_set hold the same alternative
541  /// (same index), the alternative value is copied via copy assignment.
542  ///
543  /// Otherwise, if the source and destination hold different alternatives
544  /// (different indices, but possibly the same type), the alternative of
545  /// the destination @ref error_set is destroyed in place, and the new
546  /// alternative is copy constructed.
547  ///
548  /// All alternatives *must* be both copy assignable and copy constructible
549  /// for this function to participate in overload resolution.
550  ///
551  /// ## Example
552  /// ```cpp
553  /// error_set<error1, error2> e1{std::in_place_index<0>};
554  ///
555  /// variant<error2, error2> e2{std::in_place_index<1>};
556  ///
557  /// e1 = e2;
558  ///
559  /// assert(holds_alternative<error2>(e1));
560  ///
561  /// assert(e1.index() == 1);
562  /// ```
563  constexpr error_set& operator=(const error_set&)
564 #ifndef DOXYGEN
565  noexcept(std::is_nothrow_copy_assignable_v<variant<T...>>)
566  requires(std::is_copy_assignable_v<variant<T...>>)
567  = default;
568 #else
569  CONDITIONALLY_NOEXCEPT;
570 #endif
571 
572  /// @brief Move assignment operator
573  ///
574  /// @details
575  /// Move assignment of an @ref error_set can take one of two possible code
576  /// paths.
577  ///
578  /// If the source and destination @ref error_set hold the same alternative
579  /// (same index), the alternative value is moved from the source to the
580  /// destination via move assignment.
581  ///
582  /// Otherwise, if the source and destination hold different alternatives
583  /// (different indices, but possibly the same type), the alternative of the
584  /// destination @ref error_set is destroyed in place, and the new alternative
585  /// is move constructed.
586  ///
587  /// The source @ref error_set will still contain the same alternative, but
588  /// the value of the alternative depends on the move assignment or move
589  /// constructor of the alternative's type. In general, moved values are
590  /// said to be in a valid, but unspecified, state.
591  ///
592  /// All alternatives *must* be both move assignable and move constructible
593  /// for this function to participate in overload resolution.
594  ///
595  /// ## Example
596  /// ```cpp
597  /// error_set<error1, error2> e1{std::in_place_index<0>};
598  ///
599  /// error_set<error1, error2> e2{std::in_place_index<1>};
600  ///
601  /// e1 = std::move(e2);
602  ///
603  /// assert(holds_alternative<error2>(e1));
604  ///
605  /// assert(e1.index() == 1);
606  /// ```
607  constexpr error_set& operator=(error_set&&)
608 #ifndef DOXYGEN
609  noexcept(std::is_nothrow_move_assignable_v<variant<T...>>)
610  requires(std::is_move_assignable_v<variant<T...>>)
611  = default;
612 #else
613  CONDITIONALLY_NOEXCEPT;
614 #endif
615 
616  /// @brief Forwarding assignment operator
617  ///
618  /// @details
619  /// This function assigns a value directly to an alternative. If the @ref
620  /// error_set already contains the target alternative, the value is assigned
621  /// using the alternative type's assignemnt operator. Otherwise, the target
622  /// alternative is constructed with the value.
623  ///
624  /// To avoid ambiguity to the reader, this function only participates in
625  /// overload resolution when there is only one alternative that could
626  /// possibly be assigned from the value.
627  ///
628  /// ## Example
629  /// ```cpp
630  /// error_set<error1, error2> e{};
631  ///
632  /// e = error2{};
633  ///
634  /// assert(holds_alternative<error2>(e));
635  ///
636  /// assert(e.index() == 1);
637  /// ```
638  ///
639  /// @param rhs The value to be assigned to the alternative
640  template <typename U>
641 #ifndef DOXYGEN
642  requires(!detail::is_error_set_v<std::remove_cvref_t<U>> &&
644 #endif
645  constexpr error_set& operator=(U&& rhs) {
646  set_() = std::forward<U>(rhs);
647  return *this;
648  }
649 
650  /// @brief Forwarding assignment operator with initializer list
651  ///
652  /// @details
653  /// This function assigns a `std::initializer_list` directoy to an
654  /// alternative. If the @ref error_set already contains the target
655  /// alternative, the `std::initializer_list` is assigned uting the
656  /// alternative type's assignment operator. Otherwise, the target
657  /// alternative is constructed with the `std::initializer_list`.
658  ///
659  /// To avoid ambiguity to the reader, this function only participates in
660  /// overload resolution when there is only one alternative that could
661  /// possibly be assigned from the `std::initializer_list`.
662  ///
663  /// ## Example
664  /// ```cpp
665  /// variant<error, std::vector<int>> e{};
666  ///
667  /// e = {1, 2, 3, 4, 5};
668  ///
669  /// assert(holds_alternative<std::vector<int>>(e));
670  ///
671  /// assert(e.index() == 1);
672  /// ```
673  ///
674  /// @param rhs The `std::initializer_list` to be assigned to the
675  /// alternative
676  template <typename U>
677 #ifndef DOXYGEN
679 #endif
680  constexpr error_set& operator=(std::initializer_list<U> rhs) {
681  set_() = rhs;
682  return *this;
683  }
684 
685  /// @brief Copy conversion assignment operator
686  ///
687  /// @details
688  /// This function assigns the value contained in another
689  /// @ref error_set that is a non-strict subset of the destination @ref
690  /// error_set. Each of the alternative types in the source @ref error_set
691  /// must also be an alternative type in the destination @ref error_set, but
692  /// they need not have the same index.
693  ///
694  /// ## Example
695  /// ```
696  /// const error_set<error1, error2> e1{std::in_place_index<1>};
697  ///
698  /// error_set<error2, error3, error1> e2{};
699  ///
700  /// e2 = e1;
701  ///
702  /// assert(holds_alternative<error2>(e2));
703  ///
704  /// assert(e2.index() == 0);
705  /// ```
706  template <typename... U>
707 #ifndef DOXYGEN
708  requires(!std::is_same_v<error_set<U...>, error_set<T...>> &&
710 #endif
711  constexpr error_set& operator=(const error_set<U...>& rhs) {
712  rhs.set_().visit_informed([this](auto&& value, auto info) {
713  set_()
714  .template emplace<detail::index_of_v<typename decltype(info)::type, T...>>(
715  value);
716  });
717  return *this;
718  }
719 
720  /// @brief Move conversion assignment operator
721  ///
722  /// @details
723  /// This function assigns the value contained in another
724  /// @ref error_set that is a non-strict subset of the destination @ref
725  /// error_set. Each of the alternative types in the source @ref error_set
726  /// must also be an alternative type in the destination @ref error_set, but
727  /// they need not have the same index.
728  ///
729  /// ## Example
730  /// ```
731  /// error_set<error1, error2> e1{std::in_place_index<1>};
732  ///
733  /// error_set<error2, error3, error1> e2{};
734  ///
735  /// e2 = std::move(e1);
736  ///
737  /// assert(holds_alternative<error2>(e2));
738  ///
739  /// assert(e2.index() == 0);
740  /// ```
741  template <typename... U>
742 #ifndef DOXYGEN
743  requires(!std::is_same_v<error_set<U...>, error_set<T...>> &&
745 #endif
746  // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
747  constexpr error_set& operator=(error_set<U...>&& rhs) {
748  std::move(rhs.set_()).visit_informed([this](auto&& value, auto info) {
749  set_()
750  .template emplace<detail::index_of_v<typename decltype(info)::type, T...>>(
751  info.forward(value));
752  });
753  return *this;
754  }
755 
756  /// @brief Gets the index of the contained alternative
757  ///
758  /// @details
759  /// The set of alternatives of a @ref error_set has a zero-based index based
760  /// on the order in which they are specified in the @ref error_set template
761  /// arguments.
762  ///
763  /// This index is the normalized discriminant of the @ref error_set. The
764  /// discriminant may be represented differently internally, depending on
765  /// the alternative types, so this function normalizes the discriminant by
766  /// converting it to a zero-based index in order to provide a common
767  /// interface for all @ref error_set instantiations.
768  ///
769  /// ## Example
770  /// ```cpp
771  /// error_set<error1, error2, error3, error4> e{std::in_place_index<2>};
772  ///
773  /// assert(e.index() == 2);
774  /// ```
775  ///
776  /// @return The index of the contained alternative.
777  [[nodiscard]] constexpr size_t index() const noexcept { return set_().index(); }
778 
779  /// @brief Constructs a new alternative in place by index
780  ///
781  /// @details
782  /// This function destroys the alternative that the @ref error_set contains
783  /// before the call, and constructs a new alternative with the specified
784  /// index in place.
785  ///
786  /// ## Example
787  /// ```cpp
788  /// error_set<error, std::string> e;
789  ///
790  /// e.emplace<1>(5, 'a');
791  ///
792  /// assert(holds_alternative<std::string>(e));
793  ///
794  /// assert(e.index() == 1);
795  ///
796  /// assert(get<1>(e) == "aaaaa");
797  /// ```
798  ///
799  /// @param args Constructor arguments forwarded to the new alternative
800  /// @return A reference to the new alternative, if applicable
801  template <size_t I, typename... Args>
802  constexpr
803 #ifndef DOXYGEN
804  typename detail::traits<detail::select_t<I, T...>>::reference
805 #else
806  REFERENCE
807 #endif
808  emplace(Args&&... args) {
809  return set_().template emplace<I>(std::forward<Args>(args)...);
810  }
811 
812  /// @brief Constructs a new alternative in place by index
813  ///
814  /// @details
815  /// This function destroys the alternative that the @ref error_set contains
816  /// before the call, and constructs a new alternative with the specified
817  /// index in place.
818  ///
819  /// ## Example
820  /// ```cpp
821  /// error_set<error, std::vector<int>> e;
822  ///
823  /// e.emplace<1>({1, 2, 3, 4, 5});
824  ///
825  /// assert(holds_alternative<std::vector<int>>(e));
826  ///
827  /// assert(e.index() == 1);
828  ///
829  /// assert(get<1>(e).size() == 5);
830  /// ```
831  ///
832  /// @param ilist Initializer list forward to the new alternative
833  /// @param args Constructor arguments forwarded to the new alternative
834  /// @return A reference to the new alternative, if applicable
835  template <size_t I, typename U, typename... Args>
836  constexpr
837 #ifndef DOXYGEN
838  typename detail::traits<detail::select_t<I, T...>>::reference
839 #else
840  REFERENCE
841 #endif
842  emplace(std::initializer_list<U> ilist, Args&&... args) {
843  return set_().template emplace<I>(ilist, std::forward<Args>(args)...);
844  }
845 
846  /// @brief Constructs a new alternative in place by type
847  ///
848  /// @details
849  /// This function destroy the alternative that the @ref error_set contains
850  /// before the call, and constructs a new alternative with the specified
851  /// type in place.
852  ///
853  /// ## Example
854  /// ```cpp
855  /// error_set<error, std::string> e;
856  ///
857  /// e.emplace<std::string>(5, 'a');
858  ///
859  /// assert(holds_alternative<std::string>(e));
860  ///
861  /// assert(e.index() == 1);
862  ///
863  /// assert(get<1>(e) == "aaaaa");
864  /// ```
865  ///
866  /// @param args Constructor arguments forwarded to the new alternative
867  /// @return A reference to the new alternative, if applicable
868  template <typename U, typename... Args>
869  constexpr
870 #ifndef DOXYGEN
871  typename detail::traits<U>::reference
872 #else
873  REFERENCE
874 #endif
875  emplace(Args&&... args) {
876  return set_().template emplace<U>(std::forward<Args>(args)...);
877  }
878 
879  /// @brief Constructs a new alternative in place by type
880  ///
881  /// @details
882  /// This function destroy the alternative that the @ref error_set contains
883  /// before the call, and constructs a new alternative with the specified
884  /// type in place.
885  ///
886  /// ## Example
887  /// ```cpp
888  /// error_set<error, std::vector<int>> e;
889  ///
890  /// e.emplace<std::vector<int>>({1, 2, 3, 4, 5});
891  ///
892  /// assert(holds_alternative<std::vector<int>>(e));
893  ///
894  /// assert(e.index() == 1);
895  ///
896  /// assert(get<1>(e).size() == 5);
897  /// ```
898  ///
899  /// @param ilist Initializer list forward to the new alternative
900  /// @param args Constructor arguments forwarded to the new alternative
901  /// @return A reference to the new alternative, if applicable
902  template <typename U, typename V, typename... Args>
903  constexpr
904 #ifndef DOXYGEN
905  typename detail::traits<U>::reference
906 #else
907  REFERENCE
908 #endif
909  emplace(std::initializer_list<V> ilist, Args&&... args) {
910  return set_().template emplace<U>(ilist, std::forward<Args>(args)...);
911  }
912 
913  /// @brief Alternative access operator by index
914  ///
915  /// @details
916  /// This function allows accessing alternatives by index using the square
917  /// bracket operator. Because the index must be a compile time value,
918  /// instead of passing the index directly, the index is provided as an
919  /// instance of @ref index_t.
920  ///
921  /// This operator is unchecked and does not throw an exception. Passing an
922  /// index that does not correspond to the currently contained alternative
923  /// results in undefined behavior.
924  ///
925  /// ## Example
926  /// ```cpp
927  /// error_set<error1, std::string, error2> e{std::in_place_index<1>, "oh no"};
928  ///
929  /// assert(e[index<1>] == "oh no");
930  ///
931  /// v[index<1>] = "no problem";
932  ///
933  /// assert(get<1>(v) == "no problem");
934  /// ```
935  ///
936  /// @param index A tag value that communicates a compile time index
937  /// @return A reference to the accessed alternative, if applicable
938  template <size_t I>
939  [[nodiscard]] constexpr
940 #ifndef DOXYGEN
941  typename detail::traits<detail::select_t<I, T...>>::reference
942 #else
943  REFERENCE
944 #endif
945  operator[]([[maybe_unused]] index_t<I> index) & noexcept {
946  return set_()[index];
947  }
948 
949  /// @brief Alternative access operator by index
950  ///
951  /// @details
952  /// This function allows accessing alternatives by index using the square
953  /// bracket operator. Because the index must be a compile time value,
954  /// instead of passing the index directly, the index is provided as an
955  /// instance of @ref index_t.
956  ///
957  /// This operator is unchecked and does not throw an exception. Passing an
958  /// index that does not correspond to the currently contained alternative
959  /// results in undefined behavior.
960  ///
961  /// ## Example
962  /// ```cpp
963  /// const error_set<error1, std::string, error2> e{
964  /// std::in_place_index<1>, "oh no"};
965  ///
966  /// assert(e[index<1>] == "oh no");
967  /// ```
968  ///
969  /// @param index A tag value that communicates a compile time index
970  /// @return A reference to the accessed alternative, if applicable
971  template <size_t I>
972  [[nodiscard]] constexpr
973 #ifndef DOXYGEN
974  typename detail::traits<detail::select_t<I, T...>>::const_reference
975 #else
977 #endif
978  operator[]([[maybe_unused]] index_t<I> index) const& noexcept {
979  return set_()[index];
980  }
981 
982  /// @brief Alternative access operator by index
983  ///
984  /// @details
985  /// This function allows accessing alternatives by index using the square
986  /// bracket operator. Because the index must be a compile time value,
987  /// instead of passing the index directly, the index is provided as an
988  /// instance of @ref index_t.
989  ///
990  /// This operator is unchecked and does not throw an exception. Passing an
991  /// index that does not correspond to the currently contained alternative
992  /// results in undefined behavior.
993  ///
994  /// ## Example
995  /// ```cpp
996  /// error_set<error1, std::string, error2> e{std::in_place_index<1>, "oh no"};
997  ///
998  /// assert(std::move(e)[index<1>] == "oh no");
999  /// ```
1000  ///
1001  /// @param index A tag value that communicates a compile time index
1002  /// @return A reference to the accessed alternative, if applicable
1003  template <size_t I>
1004  [[nodiscard]] constexpr
1005 #ifndef DOXYGEN
1006  typename detail::traits<detail::select_t<I, T...>>::rvalue_reference
1007 #else
1009 #endif
1010  operator[]([[maybe_unused]] index_t<I> index) && {
1011  return std::move(set_())[index];
1012  }
1013 
1014  /// @brief Alternative access operator by index
1015  ///
1016  /// @details
1017  /// This function allows accessing alternatives by index using the square
1018  /// bracket operator. Because the index must be a compile time value,
1019  /// instead of passing the index directly, the index is provided as an
1020  /// instance of @ref index_t.
1021  ///
1022  /// This operator is unchecked and does not throw an exception. Passing an
1023  /// index that does not correspond to the currently contained alternative
1024  /// results in undefined behavior.
1025  ///
1026  /// ## Example
1027  /// ```cpp
1028  /// const error_set<error1, std::string, error2> e{
1029  /// std::in_place_index<1>, "oh no"};
1030  ///
1031  /// assert(std::move(e)[index<1>] == "oh no");
1032  /// ```
1033  ///
1034  /// @param index A tag value that communicates a compile time index
1035  /// @return A reference to the accessed alternative, if applicable
1036  template <size_t I>
1037  [[nodiscard]] constexpr
1038 #ifndef DOXYGEN
1039  typename detail::traits<detail::select_t<I, T...>>::const_rvalue_reference
1040 #else
1042 #endif
1043  operator[]([[maybe_unused]] index_t<I> index) const&& {
1044  return std::move(set_())[index];
1045  }
1046 
1047  /// @brief Alternative access operator by type
1048  ///
1049  /// @details
1050  /// This function allows accessing alternatives by type using the square
1051  /// bracket operator. Because the index must be a compile time value,
1052  /// the index is provided as an instance of @ref type_t.
1053  ///
1054  /// This operator is unchecked and does not throw an exception. Passing an
1055  /// index that does not correspond to the currently contained alternative
1056  /// results in undefined behavior.
1057  ///
1058  /// ## Example
1059  /// ```cpp
1060  /// error_set<error1, std::string, error2> e{
1061  /// std::in_place_index<1>, "oh no"};
1062  ///
1063  /// assert(e[type<std::string>] == "oh no");
1064  ///
1065  /// v[type<std::string>] = "no problem";
1066  ///
1067  /// assert(get<std::string>(v) == "no problem");
1068  /// ```
1069  ///
1070  /// @param index A tag value that communicates a compile time index
1071  /// @return The accessed alternative value, if applicable
1072  template <typename U>
1073  [[nodiscard]] constexpr
1074 #ifndef DOXYGEN
1075  typename detail::traits<U>::reference
1076 #else
1077  REFERENCE
1078 #endif
1079  operator[]([[maybe_unused]] type_t<U> type) & noexcept {
1080  return set_()[type];
1081  }
1082 
1083  /// @brief Alternative access operator by type
1084  ///
1085  /// @details
1086  /// This function allows accessing alternatives by type using the square
1087  /// bracket operator. Because the index must be a compile time value,
1088  /// the index is provided as an instance of @ref type_t.
1089  ///
1090  /// This operator is unchecked and does not throw an exception. Passing an
1091  /// index that does not correspond to the currently contained alternative
1092  /// results in undefined behavior.
1093  ///
1094  /// ## Example
1095  /// ```cpp
1096  /// const error_set<error1, std::string, error2> e{
1097  /// std::in_place_index<1>, "oh no"};
1098  ///
1099  /// assert(e[type<std::string>] == "oh no");
1100  /// ```
1101  ///
1102  /// @param index A tag value that communicates a compile time index
1103  /// @return The accessed alternative value, if applicable
1104  template <typename U>
1105  [[nodiscard]] constexpr
1106 #ifndef DOXYGEN
1107  typename detail::traits<U>::const_reference
1108 #else
1110 #endif
1111  operator[]([[maybe_unused]] type_t<U> type) const& noexcept {
1112  return set_()[type];
1113  }
1114 
1115  /// @brief Alternative access operator by type
1116  ///
1117  /// @details
1118  /// This function allows accessing alternatives by type using the square
1119  /// bracket operator. Because the index must be a compile time value,
1120  /// the index is provided as an instance of @ref type_t.
1121  ///
1122  /// This operator is unchecked and does not throw an exception. Passing an
1123  /// index that does not correspond to the currently contained alternative
1124  /// results in undefined behavior.
1125  ///
1126  /// ## Example
1127  /// ```cpp
1128  /// error_set<error1, std::string, error2> e{
1129  /// std::in_place_index<1>, "oh no"};
1130  ///
1131  /// assert(std::move(e)[type<std::string>] == "oh no");
1132  /// ```
1133  ///
1134  /// @param index A tag value that communicates a compile time index
1135  /// @return The accessed alternative value, if applicable
1136  template <typename U>
1137  [[nodiscard]] constexpr
1138 #ifndef DOXYGEN
1139  typename detail::traits<U>::rvalue_reference
1140 #else
1142 #endif
1143  operator[]([[maybe_unused]] type_t<U> type) && {
1144  return std::move(set_())[type];
1145  }
1146 
1147  /// @brief Alternative access operator by type
1148  ///
1149  /// @details
1150  /// This function allows accessing alternatives by type using the square
1151  /// bracket operator. Because the index must be a compile time value,
1152  /// the index is provided as an instance of @ref type_t.
1153  ///
1154  /// This operator is unchecked and does not throw an exception. Passing an
1155  /// index that does not correspond to the currently contained alternative
1156  /// results in undefined behavior.
1157  ///
1158  /// ## Example
1159  /// ```cpp
1160  /// const error_set<error1, std::string, error2> e{
1161  /// std::in_place_index<1>, "oh no"};
1162  ///
1163  /// assert(std::move(e)[type<std::string>] == "oh no");
1164  /// ```
1165  ///
1166  /// @param index A tag value that communicates a compile time index
1167  /// @return The accessed alternative value, if applicable
1168  template <typename U>
1169  [[nodiscard]] constexpr
1170 #ifndef DOXYGEN
1171  typename detail::traits<U>::const_rvalue_reference
1172 #else
1174 #endif
1175  operator[]([[maybe_unused]] type_t<U> type) const&& {
1176  return std::move(set_())[type];
1177  }
1178 
1179  /// @brief Gets an alternative by index
1180  ///
1181  /// @details
1182  /// This function allows accessing alternatives by index, which is provided
1183  /// as a template argument.
1184  ///
1185  /// ## Example
1186  /// ```cpp
1187  /// error_set<error1, std::string, error2> e{
1188  /// std::in_place_index<1>, "oh no"};
1189  ///
1190  /// assert(e.get<1>() == "oh no");
1191  ///
1192  /// e.get<1>() = "no problem";
1193  ///
1194  /// assert(e.get<1>() == "no problem");
1195  /// ```
1196  ///
1197  /// @tparam I The index of the alternative to access.
1198  ///
1199  /// @return A reference to the accessed alternative, if applicable.
1200  ///
1201  /// @throws bad_variant_access Thrown if the @ref error_set does not contain
1202  /// the alternative with the corresponding index.
1203  template <size_t I>
1204  [[nodiscard]] constexpr
1205 #ifndef DOXYGEN
1206  typename detail::traits<detail::select_t<I, T...>>::reference
1207 #else
1208  REFERENCE
1209 #endif
1210  get() & {
1211  return set_().template get<I>();
1212  }
1213 
1214  /// @brief Gets an alternative by index
1215  ///
1216  /// @details
1217  /// This function allows accessing alternatives by index, which is provided
1218  /// as a template argument.
1219  ///
1220  /// ## Example
1221  /// ```cpp
1222  /// const error_set<error1, std::string, error2> e{
1223  /// std::in_place_index<1>, "oh no"};
1224  ///
1225  /// assert(e.get<1>() == "oh no");
1226  /// ```
1227  ///
1228  /// @tparam I The index of the alternative to access.
1229  ///
1230  /// @return A const reference to the accessed alternative, if applicable.
1231  ///
1232  /// @throws bad_variant_access Thrown if the @ref error_set does not contain
1233  /// the alternative with the corresponding index.
1234  template <size_t I>
1235  [[nodiscard]] constexpr
1236 #ifndef DOXYGEN
1237  typename detail::traits<detail::select_t<I, T...>>::const_reference
1238 #else
1240 #endif
1241  get() const& {
1242  return set_().template get<I>();
1243  }
1244 
1245  /// @brief Gets an alternative by index
1246  ///
1247  /// @details
1248  /// This function allows accessing alternatives by index, which is provided
1249  /// as a template argument.
1250  ///
1251  /// ## Example
1252  /// ```cpp
1253  /// error_set<error1, std::string, error2> e{
1254  /// std::in_place_index<1>, "oh no"};
1255  ///
1256  /// assert(std::move(e).get<1>() == "oh no");
1257  /// ```
1258  ///
1259  /// @tparam I The index of the alternative to access.
1260  ///
1261  /// @return An rvalue reference to the accessed alternative, if applicable.
1262  ///
1263  /// @throws bad_variant_access Thrown if the @ref error_set does not contain
1264  /// the alternative with the corresponding index.
1265  template <size_t I>
1266  [[nodiscard]] constexpr
1267 #ifndef DOXYGEN
1268  typename detail::traits<detail::select_t<I, T...>>::rvalue_reference
1269 #else
1271 #endif
1272  get() && {
1273  return std::move(set_()).template get<I>();
1274  }
1275 
1276  /// @brief Gets an alternative by index
1277  ///
1278  /// @details
1279  /// This function allows accessing alternatives by index, which is provided
1280  /// as a template argument.
1281  ///
1282  /// ## Example
1283  /// ```cpp
1284  /// const error_set<error1, std::string, error2> e{
1285  /// std::in_place_index<1>, "oh no"};
1286  ///
1287  /// assert(std::move(e).get<1>() == "oh no");
1288  /// ```
1289  ///
1290  /// @tparam I The index of the alternative to access.
1291  ///
1292  /// @return A const rvalue reference to the accessed alternative, if applicable.
1293  ///
1294  /// @throws bad_variant_access Thrown if the @ref error_set does not contain
1295  /// the alternative with the corresponding index.
1296  template <size_t I>
1297  [[nodiscard]] constexpr
1298 #ifndef DOXYGEN
1299  typename detail::traits<detail::select_t<I, T...>>::const_rvalue_reference
1300 #else
1302 #endif
1303  get() const&& {
1304  return std::move(set_()).template get<I>();
1305  }
1306 
1307  /// @brief Gets an alternative by type
1308  ///
1309  /// @details
1310  /// This function allows accessing alternatives by type, which is provided
1311  /// as a template argument.
1312  ///
1313  /// ## Example
1314  /// ```cpp
1315  /// error_set<error1, std::string, error2> e{
1316  /// std::in_place_index<1>, "oh no"};
1317  ///
1318  /// assert(e.get<std::string>() == "oh no");
1319  ///
1320  /// e.get<std::string>() = "no problem";
1321  ///
1322  /// assert(e.get<std::string>() == "no problem");
1323  /// ```
1324  ///
1325  /// @tparam U The type of the alternative to access.
1326  ///
1327  /// @return A reference to the accessed alternative, if applicable.
1328  ///
1329  /// @throws bad_variant_access Thrown if the @ref error_set does not contain
1330  /// the alternative with the corresponding type.
1331  template <typename U>
1332  [[nodiscard]] constexpr
1333 #ifndef DOXYGEN
1334  typename detail::traits<U>::reference
1335 #else
1336  REFERENCE
1337 #endif
1338  get() & {
1339  return set_().template get<U>();
1340  }
1341 
1342  /// @brief Gets an alternative by type
1343  ///
1344  /// @details
1345  /// This function allows accessing alternatives by type, which is provided
1346  /// as a template argument.
1347  ///
1348  /// ## Example
1349  /// ```cpp
1350  /// const error_set<error1, std::string, error2> e{
1351  /// std::in_place_index<1>, "oh no"};
1352  ///
1353  /// assert(e.get<std::string>() == "oh no");
1354  /// ```
1355  ///
1356  /// @tparam U The type of the alternative to access.
1357  ///
1358  /// @return A const reference to the accessed alternative, if applicable.
1359  ///
1360  /// @throws bad_variant_access Thrown if the @ref error_set does not contain
1361  /// the alternative with the corresponding type.
1362  template <typename U>
1363  [[nodiscard]] constexpr
1364 #ifndef DOXYGEN
1365  typename detail::traits<U>::const_reference
1366 #else
1368 #endif
1369  get() const& {
1370  return set_().template get<U>();
1371  }
1372 
1373  /// @brief Gets an alternative by type
1374  ///
1375  /// @details
1376  /// This function allows accessing alternatives by type, which is provided
1377  /// as a template argument.
1378  ///
1379  /// ## Example
1380  /// ```cpp
1381  /// error_set<error1, std::string, error2> e{
1382  /// std::in_place_index<1>, "oh no"};
1383  ///
1384  /// assert(std::move(e).get<std::string>() == "oh no");
1385  /// ```
1386  ///
1387  /// @tparam U The type of the alternative to access.
1388  ///
1389  /// @return An rvalue reference to the accessed alternative, if applicable.
1390  ///
1391  /// @throws bad_variant_access Thrown if the @ref error_set does not contain
1392  /// the alternative with the corresponding type.
1393  template <typename U>
1394  [[nodiscard]] constexpr
1395 #ifndef DOXYGEN
1396  typename detail::traits<U>::rvalue_reference
1397 #else
1399 #endif
1400  get() && {
1401  return std::move(set_()).template get<U>();
1402  }
1403 
1404  /// @brief Gets an alternative by type
1405  ///
1406  /// @details
1407  /// This function allows accessing alternatives by type, which is provided
1408  /// as a template argument.
1409  ///
1410  /// ## Example
1411  /// ```cpp
1412  /// const error_set<error1, std::string, error2> e{
1413  /// std::in_place_index<1>, "oh no"};
1414  ///
1415  /// assert(std::move(e).get<std::string>() == "oh no");
1416  /// ```
1417  ///
1418  /// @tparam U The type of the alternative to access.
1419  ///
1420  /// @return A const rvalue reference to the accessed alternative, if applicable.
1421  ///
1422  /// @throws bad_variant_access Thrown if the @ref error_set does not contain
1423  /// the alternative with the corresponding type.
1424  template <typename U>
1425  [[nodiscard]] constexpr
1426 #ifndef DOXYGEN
1427  typename detail::traits<U>::const_rvalue_reference
1428 #else
1430 #endif
1431  get() const&& {
1432  return std::move(set_()).template get<U>();
1433  }
1434 
1435  /// @brief Gets an alternative pointer by index if the @ref error_set holds it
1436  ///
1437  /// @details
1438  /// This functions tries to access an alternative by index. If the @ref
1439  /// error_set contains the alternative, this function returns a pointer to
1440  /// the alternative, if applicable. If the @ref error_set does not contain
1441  /// the alternative, this function returns null. In the case where the
1442  /// alternative is of type `void`, this function does nothing.
1443  ///
1444  /// ## Example
1445  /// ```cpp
1446  /// error_set<error1, error2, error3> e{std::in_place_index<1>};
1447  ///
1448  /// assert(e.get_if<1>() != nullptr);
1449  ///
1450  /// assert(e.get_if<0>() == nullptr);
1451  /// ```
1452  ///
1453  /// @tparam I The index of the alternative to access.
1454  ///
1455  /// @return A pointer to the accessed alternative, if applicable, or null
1456  template <size_t I>
1457  [[nodiscard]] constexpr
1458 #ifndef DOXYGEN
1459  typename detail::traits<detail::select_t<I, T...>>::pointer
1460 #else
1461  POINTER
1462 #endif
1463  get_if() noexcept {
1464  return set_().template get_if<I>();
1465  }
1466 
1467  /// @brief Gets an alternative pointer by index if the @ref error_set holds it
1468  ///
1469  /// @details
1470  /// This functions tries to access an alternative by index. If the @ref
1471  /// error_set contains the alternative, this function returns a pointer to
1472  /// the alternative, if applicable. If the @ref error_set does not contain
1473  /// the alternative, this function returns null. In the case where the
1474  /// alternative is of type `void`, this function does nothing.
1475  ///
1476  /// ## Example
1477  /// ```cpp
1478  /// const error_set<error1, error2, error3> e{std::in_place_index<1>};
1479  ///
1480  /// assert(e.get_if<1>() != nullptr);
1481  ///
1482  /// assert(e.get_if<0>() == nullptr);
1483  /// ```
1484  ///
1485  /// @tparam I The index of the alternative to access.
1486  ///
1487  /// @return A const pointer to the accessed alternative, if applicable, or null
1488  template <size_t I>
1489  [[nodiscard]] constexpr
1490 #ifndef DOXYGEN
1491  typename detail::traits<detail::select_t<I, T...>>::const_pointer
1492 #else
1494 #endif
1495  get_if() const noexcept {
1496  return set_().template get_if<I>();
1497  }
1498 
1499  /// @brief Gets an alternative pointer by type if the @ref error_set holds it
1500  ///
1501  /// @details
1502  /// This functions tries to access an alternative by type. If the @ref
1503  /// error_set contains the alternative, this function returns a pointer to
1504  /// the alternative, if applicable. If the @ref error_set does not contain
1505  /// the alternative, this function returns null. In the case where the
1506  /// alternative is of type `void`, this function does nothing.
1507  ///
1508  /// ## Example
1509  /// ```cpp
1510  /// error_set<error1, error2, error3> e{std::in_place_index<1>};
1511  ///
1512  /// assert(e.get_if<error2>() != nullptr);
1513  ///
1514  /// assert(e.get_if<error1>() == nullptr);
1515  /// ```
1516  ///
1517  /// @tparam T The type of the alternative to access.
1518  ///
1519  /// @return A pointer to the accessed alternative, if applicable, or null
1520  template <typename U>
1521 #ifndef DOXYGEN
1522  requires detail::is_unique_v<U, T...>
1523 #endif
1524  [[nodiscard]] constexpr
1525 #ifndef DOXYGEN
1526  typename detail::traits<U>::pointer
1527 #else
1528  POINTER
1529 #endif
1530  get_if() noexcept {
1531  return set_().template get_if<U>();
1532  }
1533 
1534  /// @brief Gets an alternative pointer by type if the @ref error_set holds it
1535  ///
1536  /// @details
1537  /// This functions tries to access an alternative by type. If the @ref
1538  /// error_set contains the alternative, this function returns a pointer to
1539  /// the alternative, if applicable. If the @ref error_set does not contain
1540  /// the alternative, this function returns null. In the case where the
1541  /// alternative is of type `void`, this function does nothing.
1542  ///
1543  /// ## Example
1544  /// ```cpp
1545  /// const error_set<error1, error2, error3> e{std::in_place_index<1>};
1546  ///
1547  /// assert(e.get_if<error2>() != nullptr);
1548  ///
1549  /// assert(e.get_if<error1>() == nullptr);
1550  /// ```
1551  ///
1552  /// @tparam T The type of the alternative to access.
1553  ///
1554  /// @return A const pointer to the accessed alternative, if applicable, or null
1555  template <typename U>
1556  [[nodiscard]] constexpr
1557 #ifndef DOXYGEN
1558  typename detail::traits<U>::const_pointer
1559 #else
1561 #endif
1562  get_if() const noexcept {
1563  return set_().template get_if<U>();
1564  }
1565 
1566  /// @brief Checks if an @ref error_set contains a particular alternative.
1567  ///
1568  /// @details
1569  /// Given a type parameter, this function checks if the @ref error_set currently
1570  /// holds an alternative that has the exact same type.
1571  ///
1572  /// ## Example
1573  /// ```cpp
1574  /// error_set<error1, error2, error3> e1{std::in_place_index<0>};
1575  ///
1576  /// assert(e1.holds_alternative<error1>());
1577  ///
1578  /// error_set<error1, error2, error3> e2{std::in_place_index<2>};
1579  ///
1580  /// assert(e2.holds_alternative<error1>());
1581  /// ```
1582  ///
1583  /// @return `true` if the @ref error_set holds an alternative of the given type.
1584  template <typename U>
1585  [[nodiscard]] constexpr bool holds_alternative() const noexcept {
1586  return set_().template holds_alternative<U>();
1587  }
1588 
1589  /// @brief Calls a visitor callable with the contained alternative
1590  ///
1591  /// @details
1592  /// This function calls the visitor as `std::invoke(visitor, alternative)`
1593  /// and returns the result of that call, if any. As such, `visitor` *must*
1594  /// be able to accecpt any alternative type as an argument. In the case of
1595  /// an alternative of type `void`, the visitor must be callable as
1596  /// `std::invoke(visitor, void_v)`.
1597  ///
1598  /// Note that the @ref overload function can be helpful for defining a
1599  /// visitor inline.
1600  ///
1601  /// Also note that this function is implemented as a compile-time-defined
1602  /// jump table (array of function pointers). In performance critical
1603  /// applications, be wary of any assumptions about how well or poorly your
1604  /// compiler will optimize a call to this function.
1605  ///
1606  /// ## Example
1607  /// ```cpp
1608  /// error_set<error1, error2, error3> e1{std::in_place_index<1>};
1609  ///
1610  /// e1.visit(overload(
1611  /// [](error1& e) { assert(false); },
1612  /// [](error2& e) { assert(true); },
1613  /// [](error3& e) { assert(false); }
1614  /// ));
1615  /// ```
1616  ///
1617  /// @param visitor The callable object that will be passed an alternative.
1618  /// @return The return value of the visitor, if any.
1619  template <typename V>
1620  constexpr
1621 #ifndef DOXYGEN
1622  detail::invoke_result_t<
1623  V&&,
1624  typename detail::traits<detail::select_t<0, T...>>::reference>
1625 #else
1626  DEDUCED
1627 #endif
1628  visit(V&& visitor) & {
1629  return set_().visit(std::forward<V>(visitor));
1630  }
1631 
1632  /// @brief Calls a visitor callable with the contained alternative
1633  ///
1634  /// @details
1635  /// This function calls the visitor as `std::invoke(visitor, alternative)`
1636  /// and returns the result of that call, if any. As such, `visitor` *must*
1637  /// be able to accecpt any alternative type as an argument. In the case of
1638  /// an alternative of type `void`, the visitor must be callable as
1639  /// `std::invoke(visitor, void_v)`.
1640  ///
1641  /// Note that the @ref overload function can be helpful for defining a
1642  /// visitor inline.
1643  ///
1644  /// Also note that this function is implemented as a compile-time-defined
1645  /// jump table (array of function pointers). In performance critical
1646  /// applications, be wary of any assumptions about how well or poorly your
1647  /// compiler will optimize a call to this function.
1648  ///
1649  /// ## Example
1650  /// ```cpp
1651  /// const error_set<error1, error2, error3> e1{std::in_place_index<1>};
1652  ///
1653  /// e1.visit(overload(
1654  /// [](const error1& e) { assert(false); },
1655  /// [](const error2& e) { assert(true); },
1656  /// [](const error3& e) { assert(false); }
1657  /// ));
1658  /// ```
1659  ///
1660  /// @param visitor The callable object that will be passed an alternative.
1661  /// @return The return value of the visitor, if any.
1662  template <typename V>
1663  constexpr
1664 #ifndef DOXYGEN
1665  detail::invoke_result_t<
1666  V&&,
1667  typename detail::traits<detail::select_t<0, T...>>::const_reference>
1668 #else
1669  DEDUCED
1670 #endif
1671  visit(V&& visitor) const& {
1672  return set_().visit(std::forward<V>(visitor));
1673  }
1674 
1675  /// @brief Calls a visitor callable with the contained alternative
1676  ///
1677  /// @details
1678  /// This function calls the visitor as `std::invoke(visitor, alternative)`
1679  /// and returns the result of that call, if any. As such, `visitor` *must*
1680  /// be able to accecpt any alternative type as an argument. In the case of
1681  /// an alternative of type `void`, the visitor must be callable as
1682  /// `std::invoke(visitor, void_v)`.
1683  ///
1684  /// Note that the @ref overload function can be helpful for defining a
1685  /// visitor inline.
1686  ///
1687  /// Also note that this function is implemented as a compile-time-defined
1688  /// jump table (array of function pointers). In performance critical
1689  /// applications, be wary of any assumptions about how well or poorly your
1690  /// compiler will optimize a call to this function.
1691  ///
1692  /// ## Example
1693  /// ```cpp
1694  /// error_set<error1, error2, error3> e1{std::in_place_index<1>};
1695  ///
1696  /// std::move(e1).visit(overload(
1697  /// [](error1&& e) { assert(false); },
1698  /// [](error2&& e) { assert(true); },
1699  /// [](error3&& e) { assert(false); }
1700  /// ));
1701  /// ```
1702  ///
1703  /// @param visitor The callable object that will be passed an alternative.
1704  /// @return The return value of the visitor, if any.
1705  template <typename V>
1706  constexpr
1707 #ifndef DOXYGEN
1708  detail::invoke_result_t<
1709  V&&,
1710  typename detail::traits<detail::select_t<0, T...>>::rvalue_reference>
1711 #else
1712  DEDUCED
1713 #endif
1714  visit(V&& visitor) && {
1715  return std::move(set_()).visit(std::forward<V>(visitor));
1716  }
1717 
1718  /// @brief Calls a visitor callable with the contained alternative
1719  ///
1720  /// @details
1721  /// This function calls the visitor as `std::invoke(visitor, alternative)`
1722  /// and returns the result of that call, if any. As such, `visitor` *must*
1723  /// be able to accecpt any alternative type as an argument. In the case of
1724  /// an alternative of type `void`, the visitor must be callable as
1725  /// `std::invoke(visitor, void_v)`.
1726  ///
1727  /// Note that the @ref overload function can be helpful for defining a
1728  /// visitor inline.
1729  ///
1730  /// Also note that this function is implemented as a compile-time-defined
1731  /// jump table (array of function pointers). In performance critical
1732  /// applications, be wary of any assumptions about how well or poorly your
1733  /// compiler will optimize a call to this function.
1734  ///
1735  /// ## Example
1736  /// ```cpp
1737  /// const error_set<error1, error2, error3> e1{std::in_place_index<1>};
1738  ///
1739  /// std::move(e1).visit(overload(
1740  /// [](const error1&& e) { assert(false); },
1741  /// [](const error2&& e) { assert(true); },
1742  /// [](const error3&& e) { assert(false); }
1743  /// ));
1744  /// ```
1745  ///
1746  /// @param visitor The callable object that will be passed an alternative.
1747  /// @return The return value of the visitor, if any.
1748  template <typename V>
1749  constexpr
1750 #ifndef DOXYGEN
1751  detail::invoke_result_t<
1752  V&&,
1753  typename detail::traits<detail::select_t<0, T...>>::const_rvalue_reference>
1754 #else
1755  DEDUCED
1756 #endif
1757  visit(V&& visitor) const&& {
1758  return std::move(set_()).visit(std::forward<V>(visitor));
1759  }
1760 
1761  /// @brief Calls a visitor callable with the contained alternative and metadata
1762  ///
1763  /// @details
1764  /// This function calls the visitor as `std::invoke(visitor, alternative, info)`,
1765  /// and returns the result of that call, if any. As such, `visitor` *must* be
1766  /// able to accept any alternative type as an argument. In the case of an
1767  /// alternative of type `void`, the visitor must be callable as
1768  /// `std::invoke(visitor, void_v, info)`.
1769  ///
1770  /// The `info` argument passed to the visitor, which differentiates this
1771  /// function from `.visit(...)`, communicates `constexpr` information about
1772  /// the alternative being visited. The type of the `info` object is not
1773  /// meant to be named, but it has the API shown below. Note that `info` is
1774  /// always an empty type.
1775  ///
1776  /// ```
1777  /// struct alternative_info {
1778  /// // index of the alternative in the source error_set
1779  /// static inline constexpr size_t index = ...;
1780  ///
1781  /// // type of the alternative as declared in the source error_set
1782  /// using type = ...;
1783  ///
1784  /// // helper function for forwarding non-const alternative values
1785  /// // without needing to provide a template argument.
1786  /// static constexpr decltype(auto) forward(...);
1787  /// };
1788  /// ```
1789  ///
1790  /// Note that the @ref overload function can be helpful for defining a
1791  /// visitor inline.
1792  ///
1793  /// Also note that this function is implemented as a compile-time-defined
1794  /// jump table (array of function pointers). In performance critical
1795  /// applications, be wary of any assumptions about how well or poorly your
1796  /// compiler will optimize a call to this function.
1797  ///
1798  /// ## Example
1799  /// ```
1800  /// error_set<error1, error2, error3> e1{std::in_place_index<1>};
1801  ///
1802  /// e1.visit_informed([](auto& value, auto info) {
1803  /// if constexpr (info.index == 0) {
1804  /// assert(false);
1805  /// } else if constexpr (info.index == 1) {
1806  /// assert(true);
1807  /// } else if constexpr (info.index == 2) {
1808  /// assert(false);
1809  /// }
1810  /// });
1811  /// ```
1812  ///
1813  /// @param visitor The callable object that will be passed an alternative.
1814  /// @return The return value of the visitor, if any.
1815  template <typename V>
1816  constexpr
1817 #ifndef DOXYGEN
1818  detail::invoke_result_t<
1819  V&&,
1820  typename detail::traits<detail::select_t<0, T...>>::reference,
1821  detail::alternative_info<0, variant<T...>>>
1822 #else
1823  DEDUCED
1824 #endif
1825  visit_informed(V&& visitor) & {
1826  return set_().visit_informed(std::forward<V>(visitor));
1827  }
1828 
1829  /// @brief Calls a visitor callable with the contained alternative and metadata
1830  ///
1831  /// @details
1832  /// This function calls the visitor as `std::invoke(visitor, alternative, info)`,
1833  /// and returns the result of that call, if any. As such, `visitor` *must* be
1834  /// able to accept any alternative type as an argument. In the case of an
1835  /// alternative of type `void`, the visitor must be callable as
1836  /// `std::invoke(visitor, void_v, info)`.
1837  ///
1838  /// The `info` argument passed to the visitor, which differentiates this
1839  /// function from `.visit(...)`, communicates `constexpr` information about
1840  /// the alternative being visited. The type of the `info` object is not
1841  /// meant to be named, but it has the API shown below. Note that `info` is
1842  /// always an empty type.
1843  ///
1844  /// ```
1845  /// struct alternative_info {
1846  /// // index of the alternative in the source error_set
1847  /// static inline constexpr size_t index = ...;
1848  ///
1849  /// // type of the alternative as declared in the source error_set
1850  /// using type = ...;
1851  ///
1852  /// // helper function for forwarding non-const alternative values
1853  /// // without needing to provide a template argument.
1854  /// static constexpr decltype(auto) forward(...);
1855  /// };
1856  /// ```
1857  ///
1858  /// Note that the @ref overload function can be helpful for defining a
1859  /// visitor inline.
1860  ///
1861  /// Also note that this function is implemented as a compile-time-defined
1862  /// jump table (array of function pointers). In performance critical
1863  /// applications, be wary of any assumptions about how well or poorly your
1864  /// compiler will optimize a call to this function.
1865  ///
1866  /// ## Example
1867  /// ```
1868  /// const error_set<error1, error2, error3> e1{std::in_place_index<1>};
1869  ///
1870  /// e1.visit_informed([](const auto& value, auto info) {
1871  /// if constexpr (info.index == 0) {
1872  /// assert(false);
1873  /// } else if constexpr (info.index == 1) {
1874  /// assert(true);
1875  /// } else if constexpr (info.index == 2) {
1876  /// assert(false);
1877  /// }
1878  /// });
1879  /// ```
1880  ///
1881  /// @param visitor The callable object that will be passed an alternative.
1882  /// @return The return value of the visitor, if any.
1883  template <typename V>
1884  constexpr
1885 #ifndef DOXYGEN
1886  detail::invoke_result_t<
1887  V&&,
1888  typename detail::traits<detail::select_t<0, T...>>::const_reference,
1889  detail::alternative_info<0, variant<T...>>>
1890 #else
1891  DEDUCED
1892 #endif
1893  visit_informed(V&& visitor) const& {
1894  return set_().visit_informed(std::forward<V>(visitor));
1895  }
1896 
1897  /// @brief Calls a visitor callable with the contained alternative and metadata
1898  ///
1899  /// @details
1900  /// This function calls the visitor as `std::invoke(visitor, alternative, info)`,
1901  /// and returns the result of that call, if any. As such, `visitor` *must* be
1902  /// able to accept any alternative type as an argument. In the case of an
1903  /// alternative of type `void`, the visitor must be callable as
1904  /// `std::invoke(visitor, void_v, info)`.
1905  ///
1906  /// The `info` argument passed to the visitor, which differentiates this
1907  /// function from `.visit(...)`, communicates `constexpr` information about
1908  /// the alternative being visited. The type of the `info` object is not
1909  /// meant to be named, but it has the API shown below. Note that `info` is
1910  /// always an empty type.
1911  ///
1912  /// ```
1913  /// struct alternative_info {
1914  /// // index of the alternative in the source error_set
1915  /// static inline constexpr size_t index = ...;
1916  ///
1917  /// // type of the alternative as declared in the source error_set
1918  /// using type = ...;
1919  ///
1920  /// // helper function for forwarding non-const alternative values
1921  /// // without needing to provide a template argument.
1922  /// static constexpr decltype(auto) forward(...);
1923  /// };
1924  /// ```
1925  ///
1926  /// Note that the @ref overload function can be helpful for defining a
1927  /// visitor inline.
1928  ///
1929  /// Also note that this function is implemented as a compile-time-defined
1930  /// jump table (array of function pointers). In performance critical
1931  /// applications, be wary of any assumptions about how well or poorly your
1932  /// compiler will optimize a call to this function.
1933  ///
1934  /// ## Example
1935  /// ```
1936  /// error_set<error1, error2, error3> e1{std::in_place_index<1>};
1937  ///
1938  /// std::move(e1).visit_informed([](auto&& value, auto info) {
1939  /// if constexpr (info.index == 0) {
1940  /// assert(false);
1941  /// } else if constexpr (info.index == 1) {
1942  /// assert(true);
1943  /// } else if constexpr (info.index == 2) {
1944  /// assert(false);
1945  /// }
1946  /// });
1947  /// ```
1948  ///
1949  /// @param visitor The callable object that will be passed an alternative.
1950  /// @return The return value of the visitor, if any.
1951  template <typename V>
1952  constexpr
1953 #ifndef DOXYGEN
1954  detail::invoke_result_t<
1955  V&&,
1956  typename detail::traits<detail::select_t<0, T...>>::rvalue_reference,
1957  detail::alternative_info<0, variant<T...>>>
1958 #else
1959  DEDUCED
1960 #endif
1961  visit_informed(V&& visitor) && {
1962  return std::move(set_()).visit_informed(std::forward<V>(visitor));
1963  }
1964 
1965  /// @brief Calls a visitor callable with the contained alternative and metadata
1966  ///
1967  /// @details
1968  /// This function calls the visitor as `std::invoke(visitor, alternative, info)`,
1969  /// and returns the result of that call, if any. As such, `visitor` *must* be
1970  /// able to accept any alternative type as an argument. In the case of an
1971  /// alternative of type `void`, the visitor must be callable as
1972  /// `std::invoke(visitor, void_v, info)`.
1973  ///
1974  /// The `info` argument passed to the visitor, which differentiates this
1975  /// function from `.visit(...)`, communicates `constexpr` information about
1976  /// the alternative being visited. The type of the `info` object is not
1977  /// meant to be named, but it has the API shown below. Note that `info` is
1978  /// always an empty type.
1979  ///
1980  /// ```
1981  /// struct alternative_info {
1982  /// // index of the alternative in the source error_set
1983  /// static inline constexpr size_t index = ...;
1984  ///
1985  /// // type of the alternative as declared in the source error_set
1986  /// using type = ...;
1987  ///
1988  /// // helper function for forwarding non-const alternative values
1989  /// // without needing to provide a template argument.
1990  /// static constexpr decltype(auto) forward(...);
1991  /// };
1992  /// ```
1993  ///
1994  /// Note that the @ref overload function can be helpful for defining a
1995  /// visitor inline.
1996  ///
1997  /// Also note that this function is implemented as a compile-time-defined
1998  /// jump table (array of function pointers). In performance critical
1999  /// applications, be wary of any assumptions about how well or poorly your
2000  /// compiler will optimize a call to this function.
2001  ///
2002  /// ## Example
2003  /// ```
2004  /// const error_set<error1, error2, error3> e1{std::in_place_index<1>};
2005  ///
2006  /// std::move(e1).visit_informed([](const auto&& value, auto info) {
2007  /// if constexpr (info.index == 0) {
2008  /// assert(false);
2009  /// } else if constexpr (info.index == 1) {
2010  /// assert(true);
2011  /// } else if constexpr (info.index == 2) {
2012  /// assert(false);
2013  /// }
2014  /// });
2015  /// ```
2016  ///
2017  /// @param visitor The callable object that will be passed an alternative.
2018  /// @return The return value of the visitor, if any.
2019  template <typename V>
2020  constexpr
2021 #ifndef DOXYGEN
2022  detail::invoke_result_t<
2023  V&&,
2024  typename detail::traits<detail::select_t<0, T...>>::const_rvalue_reference,
2025  detail::alternative_info<0, variant<T...>>>
2026 #else
2027  DEDUCED
2028 #endif
2029  visit_informed(V&& visitor) const&& {
2030  return std::move(set_()).visit_informed(std::forward<V>(visitor));
2031  }
2032 
2033  /// @brief Swaps two @ref error_set instances
2034  ///
2035  /// @details
2036  /// If the two @ref error_set instances contain the same alternative, the
2037  /// alternative values are swapped directly. Otherwise, the alternatives
2038  /// are swapped by moving out of the variants, destroying the old
2039  /// alternatives, and move constructed into the new alternatives.
2040  ///
2041  /// ## Example
2042  /// ```cpp
2043  /// error_set<error1, error2, error3> e1{std::in_place_index<0>};
2044  ///
2045  /// error_set<error1, error2, error3> e2{std::in_place_index<1>};
2046  ///
2047  /// e1.swap(e2);
2048  ///
2049  /// assert(e1.index() == 1);
2050  ///
2051  /// assert(e2.index() == 0);
2052  /// ```
2053  ///
2054  /// @param other The "other" @ref error_set to swap with this @ref error_set
2055  constexpr void swap(error_set& other)
2056 #ifndef DOXYGEN
2057  noexcept(noexcept(set_().swap(other.set_())))
2058 #else
2060 #endif
2061  {
2062  set_().swap(other.set_());
2063  }
2064 };
2065 
2066 /// @relates error_set
2067 /// @brief Checks if an @ref error_set contains a particular alternative.
2068 ///
2069 /// @details
2070 /// Given a type parameter, this function checks if the @ref error_set currently
2071 /// holds an alternative that has the exact same type.
2072 ///
2073 /// ## Example
2074 /// ```cpp
2075 /// error_set<error1, error2, error3> e1{std::in_place_index<0>};
2076 ///
2077 /// assert(holds_alternative<error1>(e1));
2078 ///
2079 /// error_set<error1, error2, error3> e2{std::in_place_index<2>};
2080 ///
2081 /// assert(holds_alternative<error1>(e2));
2082 /// ```
2083 ///
2084 /// @return `true` if the @ref error_set holds an alternative of the given type.
2085 template <typename T, typename... U>
2086 constexpr bool holds_alternative(const error_set<U...>& e) noexcept {
2087  return e.template holds_alternative<T>();
2088 }
2089 
2090 /// @relates error_set
2091 /// @brief Gets an alternative by index
2092 ///
2093 /// @details
2094 /// This function allows accessing alternatives by index, which is provided
2095 /// as a template argument.
2096 ///
2097 /// ## Example
2098 /// ```cpp
2099 /// error_set<error1, std::string, error2> e{
2100 /// std::in_place_index<1>, "oh no"};
2101 ///
2102 /// assert(get<1>(e) == "oh no");
2103 ///
2104 /// get<1>(e) = "no problem";
2105 ///
2106 /// assert(get<1>(e) == "no problem");
2107 /// ```
2108 ///
2109 /// @tparam I The index of the alternative to access.
2110 ///
2111 /// @return A reference to the accessed alternative, if applicable.
2112 ///
2113 /// @throws bad_variant_access Thrown if the @ref error_set does not contain
2114 /// the alternative with the corresponding index.
2115 template <size_t I, typename... T>
2116 constexpr
2117 #ifndef DOXYGEN
2118  typename detail::traits<detail::select_t<I, T...>>::reference
2119 #else
2120  REFERENCE
2121 #endif
2122  get(error_set<T...>& e) {
2123  return e.template get<I>();
2124 }
2125 
2126 /// @relates error_set
2127 /// @brief Gets an alternative by index
2128 ///
2129 /// @details
2130 /// This function allows accessing alternatives by index, which is provided
2131 /// as a template argument.
2132 ///
2133 /// ## Example
2134 /// ```cpp
2135 /// const error_set<error1, std::string, error2> e{
2136 /// std::in_place_index<1>, "oh no"};
2137 ///
2138 /// assert(get<1>(e) == "oh no");
2139 /// ```
2140 ///
2141 /// @tparam I The index of the alternative to access.
2142 ///
2143 /// @return A const reference to the accessed alternative, if applicable.
2144 ///
2145 /// @throws bad_variant_access Thrown if the @ref error_set does not contain
2146 /// the alternative with the corresponding index.
2147 template <size_t I, typename... T>
2148 constexpr
2149 #ifndef DOXYGEN
2150  typename detail::traits<detail::select_t<I, T...>>::const_reference
2151 #else
2153 #endif
2154  get(const error_set<T...>& e) {
2155  return e.template get<I>();
2156 }
2157 
2158 /// @relates error_set
2159 /// @brief Gets an alternative by index
2160 ///
2161 /// @details
2162 /// This function allows accessing alternatives by index, which is provided
2163 /// as a template argument.
2164 ///
2165 /// ## Example
2166 /// ```cpp
2167 /// error_set<error1, std::string, error2> e{
2168 /// std::in_place_index<1>, "oh no"};
2169 ///
2170 /// assert(get<1>(std::move(e)) == "oh no");
2171 /// ```
2172 ///
2173 /// @tparam I The index of the alternative to access.
2174 ///
2175 /// @return An rvalue reference to the accessed alternative, if applicable.
2176 ///
2177 /// @throws bad_variant_access Thrown if the @ref error_set does not contain
2178 /// the alternative with the corresponding index.
2179 template <size_t I, typename... T>
2180 constexpr
2181 #ifndef DOXYGEN
2182  typename detail::traits<detail::select_t<I, T...>>::rvalue_reference
2183 #else
2185 #endif
2186  get(error_set<T...>&& e) {
2187  return std::move(e).template get<I>();
2188 }
2189 
2190 /// @relates error_set
2191 /// @brief Gets an alternative by index
2192 ///
2193 /// @details
2194 /// This function allows accessing alternatives by index, which is provided
2195 /// as a template argument.
2196 ///
2197 /// ## Example
2198 /// ```cpp
2199 /// const error_set<error1, std::string, error2> e{
2200 /// std::in_place_index<1>, "oh no"};
2201 ///
2202 /// assert(get<1>(std::move(e)) == "oh no");
2203 /// ```
2204 ///
2205 /// @tparam I The index of the alternative to access.
2206 ///
2207 /// @return A const rvalue reference to the accessed alternative, if applicable.
2208 ///
2209 /// @throws bad_variant_access Thrown if the @ref error_set does not contain
2210 /// the alternative with the corresponding index.
2211 template <size_t I, typename... T>
2212 constexpr
2213 #ifndef DOXYGEN
2214  typename detail::traits<detail::select_t<I, T...>>::const_rvalue_reference
2215 #else
2217 #endif
2218  get(const error_set<T...>&& e) {
2219  return std::move(e).template get<I>();
2220 }
2221 
2222 /// @relates error_set
2223 /// @brief Gets an alternative by type
2224 ///
2225 /// @details
2226 /// This function allows accessing alternatives by type, which is provided
2227 /// as a template argument.
2228 ///
2229 /// ## Example
2230 /// ```cpp
2231 /// error_set<error1, std::string, error2> e{
2232 /// std::in_place_index<1>, "oh no"};
2233 ///
2234 /// assert(get<std::string>(e) == "oh no");
2235 ///
2236 /// get<std::string>(e) = "no problem";
2237 ///
2238 /// assert(get<std::string>(e) == "no problem");
2239 /// ```
2240 ///
2241 /// @tparam U The type of the alternative to access.
2242 ///
2243 /// @return A reference to the accessed alternative, if applicable.
2244 ///
2245 /// @throws bad_variant_access Thrown if the @ref error_set does not contain
2246 /// the alternative with the corresponding type.
2247 template <typename T, typename... U>
2248 constexpr
2249 #ifndef DOXYGEN
2250  typename detail::traits<T>::reference
2251 #else
2252  REFERENCE
2253 #endif
2254  get(error_set<U...>& e) {
2255  return e.template get<T>();
2256 }
2257 
2258 /// @relates error_set
2259 /// @brief Gets an alternative by type
2260 ///
2261 /// @details
2262 /// This function allows accessing alternatives by type, which is provided
2263 /// as a template argument.
2264 ///
2265 /// ## Example
2266 /// ```cpp
2267 /// const error_set<error1, std::string, error2> e{
2268 /// std::in_place_index<1>, "oh no"};
2269 ///
2270 /// assert(get<std::string>(e) == "oh no");
2271 /// ```
2272 ///
2273 /// @tparam U The type of the alternative to access.
2274 ///
2275 /// @return A const reference to the accessed alternative, if applicable.
2276 ///
2277 /// @throws bad_variant_access Thrown if the @ref error_set does not contain
2278 /// the alternative with the corresponding type.
2279 template <typename T, typename... U>
2280 constexpr
2281 #ifndef DOXYGEN
2282  typename detail::traits<T>::const_reference
2283 #else
2285 #endif
2286  get(const error_set<U...>& e) {
2287  return e.template get<T>();
2288 }
2289 
2290 /// @relates error_set
2291 /// @brief Gets an alternative by type
2292 ///
2293 /// @details
2294 /// This function allows accessing alternatives by type, which is provided
2295 /// as a template argument.
2296 ///
2297 /// ## Example
2298 /// ```cpp
2299 /// error_set<error1, std::string, error2> e{
2300 /// std::in_place_index<1>, "oh no"};
2301 ///
2302 /// assert(get<std::string>(std::move(e)) == "oh no");
2303 /// ```
2304 ///
2305 /// @tparam U The type of the alternative to access.
2306 ///
2307 /// @return An rvalue reference to the accessed alternative, if applicable.
2308 ///
2309 /// @throws bad_variant_access Thrown if the @ref error_set does not contain
2310 /// the alternative with the corresponding type.
2311 template <typename T, typename... U>
2312 constexpr
2313 #ifndef DOXYGEN
2314  typename detail::traits<T>::rvalue_reference
2315 #else
2317 #endif
2318  get(error_set<U...>&& e) {
2319  return std::move(e).template get<T>();
2320 }
2321 
2322 /// @relates error_set
2323 /// @brief Gets an alternative by type
2324 ///
2325 /// @details
2326 /// This function allows accessing alternatives by type, which is provided
2327 /// as a template argument.
2328 ///
2329 /// ## Example
2330 /// ```cpp
2331 /// const error_set<error1, std::string, error2> e{
2332 /// std::in_place_index<1>, "oh no"};
2333 ///
2334 /// assert(std::move(e).get<std::string>() == "oh no");
2335 /// ```
2336 ///
2337 /// @tparam U The type of the alternative to access.
2338 ///
2339 /// @return A const rvalue reference to the accessed alternative, if applicable.
2340 ///
2341 /// @throws bad_variant_access Thrown if the @ref error_set does not contain
2342 /// the alternative with the corresponding type.
2343 template <typename T, typename... U>
2344 constexpr
2345 #ifndef DOXYGEN
2346  typename detail::traits<T>::const_rvalue_reference
2347 #else
2349 #endif
2350  get(const error_set<U...>&& e) {
2351  return std::move(e).template get<T>();
2352 }
2353 
2354 /// @relates error_set
2355 /// @brief Gets an alternative pointer by index if the @ref error_set holds it
2356 ///
2357 /// @details
2358 /// This functions tries to access an alternative by index. If the @ref
2359 /// error_set contains the alternative, this function returns a pointer to
2360 /// the alternative, if applicable. If the @ref error_set does not contain
2361 /// the alternative, this function returns null. In the case where the
2362 /// alternative is of type `void`, this function does nothing.
2363 ///
2364 /// ## Example
2365 /// ```cpp
2366 /// error_set<error1, error2, error3> e{std::in_place_index<1>};
2367 ///
2368 /// assert(get_if<1>(e) != nullptr);
2369 ///
2370 /// assert(get_if<0>(e) == nullptr);
2371 /// ```
2372 ///
2373 /// @tparam I The index of the alternative to access.
2374 ///
2375 /// @return A pointer to the accessed alternative, if applicable, or null
2376 template <size_t I, typename... T>
2377 constexpr
2378 #ifndef DOXYGEN
2379  typename detail::traits<detail::select_t<I, T...>>::pointer
2380 #else
2381  POINTER
2382 #endif
2383  get_if(error_set<T...>& e) noexcept {
2384  return e.template get_if<I>();
2385 }
2386 
2387 /// @relates error_set
2388 /// @brief Gets an alternative pointer by index if the @ref error_set holds it
2389 ///
2390 /// @details
2391 /// This functions tries to access an alternative by index. If the @ref
2392 /// error_set contains the alternative, this function returns a pointer to
2393 /// the alternative, if applicable. If the @ref error_set does not contain
2394 /// the alternative, this function returns null. In the case where the
2395 /// alternative is of type `void`, this function does nothing.
2396 ///
2397 /// ## Example
2398 /// ```cpp
2399 /// const error_set<error1, error2, error3> e{std::in_place_index<1>};
2400 ///
2401 /// assert(get_if<1>(e) != nullptr);
2402 ///
2403 /// assert(get_if<0>(e) == nullptr);
2404 /// ```
2405 ///
2406 /// @tparam I The index of the alternative to access.
2407 ///
2408 /// @return A const pointer to the accessed alternative, if applicable, or null
2409 template <size_t I, typename... T>
2410 constexpr
2411 #ifndef DOXYGEN
2412  typename detail::traits<detail::select_t<I, T...>>::const_pointer
2413 #else
2415 #endif
2416  get_if(const error_set<T...>& e) noexcept {
2417  return e.template get_if<I>();
2418 }
2419 
2420 /// @relates error_set
2421 /// @brief Gets an alternative pointer by type if the @ref error_set holds it
2422 ///
2423 /// @details
2424 /// This functions tries to access an alternative by type. If the @ref
2425 /// error_set contains the alternative, this function returns a pointer to
2426 /// the alternative, if applicable. If the @ref error_set does not contain
2427 /// the alternative, this function returns null. In the case where the
2428 /// alternative is of type `void`, this function does nothing.
2429 ///
2430 /// ## Example
2431 /// ```cpp
2432 /// error_set<error1, error2, error3> e{std::in_place_index<1>};
2433 ///
2434 /// assert(get_if<error2>(e) != nullptr);
2435 ///
2436 /// assert(get_if<error1>(e) == nullptr);
2437 /// ```
2438 ///
2439 /// @tparam T The type of the alternative to access.
2440 ///
2441 /// @return A pointer to the accessed alternative, if applicable, or null
2442 template <typename T, typename... U>
2443 constexpr
2444 #ifndef DOXYGEN
2445  typename detail::traits<T>::pointer
2446 #else
2447  POINTER
2448 #endif
2449  get_if(error_set<U...>& e) noexcept {
2450  return e.template get_if<T>();
2451 }
2452 
2453 /// @relates error_set
2454 /// @brief Gets an alternative pointer by type if the @ref error_set holds it
2455 ///
2456 /// @details
2457 /// This functions tries to access an alternative by type. If the @ref
2458 /// error_set contains the alternative, this function returns a pointer to
2459 /// the alternative, if applicable. If the @ref error_set does not contain
2460 /// the alternative, this function returns null. In the case where the
2461 /// alternative is of type `void`, this function does nothing.
2462 ///
2463 /// ## Example
2464 /// ```cpp
2465 /// const error_set<error1, error2, error3> e{std::in_place_index<1>};
2466 ///
2467 /// assert(get_if<error2>(e) != nullptr);
2468 ///
2469 /// assert(get_if<error1>(e) == nullptr);
2470 /// ```
2471 ///
2472 /// @tparam T The type of the alternative to access.
2473 ///
2474 /// @return A const pointer to the accessed alternative, if applicable, or null
2475 template <typename T, typename... U>
2476 constexpr
2477 #ifndef DOXYGEN
2478  typename detail::traits<T>::const_pointer
2479 #else
2481 #endif
2482  get_if(const error_set<U...>& e) noexcept {
2483  return e.template get_if<T>();
2484 }
2485 
2486 /// @relates error_set
2487 /// @brief Swaps two @ref error_set instances
2488 ///
2489 /// @details
2490 /// If the two @ref error_set instances contain the same alternative, the
2491 /// alternative values are swapped directly. Otherwise, the alternatives
2492 /// are swapped by moving out of the variants, destroying the old
2493 /// alternatives, and move constructed into the new alternatives.
2494 ///
2495 /// ## Example
2496 /// ```cpp
2497 /// error_set<error1, error2, error3> e1{std::in_place_index<0>};
2498 ///
2499 /// error_set<error1, error2, error3> e2{std::in_place_index<1>};
2500 ///
2501 /// swap(e1, e2);
2502 ///
2503 /// assert(e1.index() == 1);
2504 ///
2505 /// assert(e2.index() == 0);
2506 /// ```
2507 ///
2508 /// @param other The "other" @ref error_set to swap with this @ref error_set
2509 template <typename... T>
2510 constexpr void swap(error_set<T...>& a, error_set<T...>& b)
2511 #ifndef DOXYGEN
2512  noexcept(noexcept(a.swap(b)))
2513 #else
2515 #endif
2516 {
2517  a.swap(b);
2518 }
2519 
2520 namespace detail {
2521 
2522 template <typename T>
2523 struct error_set_size_helper;
2524 
2525 template <typename... T>
2526 struct error_set_size_helper<error_set<T...>>
2527  : std::integral_constant<size_t, sizeof...(T)> {};
2528 
2529 template <typename... T>
2530 struct error_set_size_helper<const error_set<T...>>
2531  : std::integral_constant<size_t, sizeof...(T)> {};
2532 
2533 template <size_t I, typename T>
2534 struct error_set_alternative_helper;
2535 
2536 template <size_t I, typename... T>
2537 struct error_set_alternative_helper<I, error_set<T...>> {
2538  using type = detail::select_t<I, T...>;
2539 };
2540 
2541 template <size_t I, typename... T>
2542 struct error_set_alternative_helper<I, const error_set<T...>>
2543  : error_set_alternative_helper<I, error_set<T...>> {};
2544 
2545 template <typename T, typename U>
2546 struct merge_error_set_helper;
2547 
2548 template <typename... T, typename U>
2549  requires(all_unique_v<T..., U>)
2550 struct merge_error_set_helper<error_set<T...>, error_set<U>> {
2551  using type = error_set<T..., U>;
2552 };
2553 
2554 template <typename... T, typename U>
2555  requires(!all_unique_v<T..., U>)
2556 struct merge_error_set_helper<error_set<T...>, error_set<U>> {
2557  using type = error_set<T...>;
2558 };
2559 
2560 template <typename... T, typename U0, typename... UN>
2561  requires(all_unique_v<T..., U0>)
2562 struct merge_error_set_helper<error_set<T...>, error_set<U0, UN...>>
2563  : merge_error_set_helper<error_set<T..., U0>, error_set<UN...>> {};
2564 
2565 template <typename... T, typename U0, typename... UN>
2566  requires(!all_unique_v<T..., U0>)
2567 struct merge_error_set_helper<error_set<T...>, error_set<U0, UN...>>
2568  : merge_error_set_helper<error_set<T...>, error_set<UN...>> {};
2569 
2570 template <typename T, typename U>
2571  requires(!is_error_set_v<std::remove_cvref_t<T>> &&
2572  !is_error_set_v<std::remove_cvref_t<U>>)
2573 struct merge_error_set_helper<T, U> : merge_error_set_helper<error_set<T>, error_set<U>> {};
2574 
2575 template <typename T, typename U>
2576  requires(is_error_set_v<std::remove_cvref_t<T>> &&
2577  !is_error_set_v<std::remove_cvref_t<U>>)
2578 struct merge_error_set_helper<T, U> : merge_error_set_helper<T, error_set<U>> {};
2579 
2580 template <typename T, typename U>
2581  requires(!is_error_set_v<std::remove_cvref_t<T>> &&
2582  is_error_set_v<std::remove_cvref_t<U>>)
2583 struct merge_error_set_helper<T, U> : merge_error_set_helper<error_set<T>, U> {};
2584 
2585 template <typename... T>
2586 struct make_error_set_helper;
2587 
2588 template <typename... T>
2589 struct make_error_set_helper<error_set<T...>> {
2590  using type = error_set<T...>;
2591 };
2592 
2593 template <typename T>
2594  requires(!is_error_set_v<T>)
2595 struct make_error_set_helper<T> {
2596  using type = error_set<T>;
2597 };
2598 
2599 template <typename T0, typename T1, typename... TN>
2600 struct make_error_set_helper<T0, T1, TN...>
2601  : make_error_set_helper<typename merge_error_set_helper<T0, T1>::type, TN...> {};
2602 
2603 } // namespace detail
2604 
2605 /// @relates error_set
2606 /// @class is_subset_of error_set.hpp <sumty/error_set.hpp>
2607 /// @brief Utility to test that one @ref error_set is a subset of another.
2608 ///
2609 /// @details
2610 /// This type is a template metaprogramming utility to test that the @ref
2611 /// error_set `ES1` is a subset of the @ref error_set `ES2`. If every
2612 /// alternative of `ES1` is an alternative of `ES2`, regardless of order or
2613 /// index, then `ES1` is a subset of `ES2`. The static member constant `value`
2614 /// is a `bool` that is `true` if `ES1` is a subset of `ES2`.
2615 ///
2616 /// ## Example
2617 /// ```
2618 /// assert(is_subset_of<
2619 /// error_set<error1>,
2620 /// error_set<error3, error2, error2>
2621 /// >::value);
2622 ///
2623 /// assert(!is_subset_of<
2624 /// error_set<error3, error2, error2>
2625 /// error_set<error1>,
2626 /// >::value);
2627 /// ```
2628 ///
2629 /// @tparam ES1 The proposed subset
2630 /// @tparam ES2 The proposed superset
2631 template <typename ES1, typename ES2>
2632 struct is_subset_of : detail::is_subset_of_impl<ES1, ES2> {};
2633 
2634 /// @relates is_subset_of
2635 /// @brief Utility to test that one @ref error_set is a subset of another.
2636 ///
2637 /// @details
2638 /// This is a template metaprogramming utility to test that the @ref
2639 /// error_set, `ES1`, is a subset of the @ref error_set `ES2`. If every
2640 /// alternative of `ES1` is an alternative of `ES2`, regardless of order or
2641 /// index, then `ES1` is a subset of `ES2`.
2642 ///
2643 /// ## Example
2644 /// ```
2645 /// assert(is_subset_of_v<
2646 /// error_set<error1>,
2647 /// error_set<error3, error2, error2>
2648 /// >);
2649 ///
2650 /// assert(!is_subset_of_v<
2651 /// error_set<error3, error2, error2>
2652 /// error_set<error1>,
2653 /// >);
2654 /// ```
2655 ///
2656 /// @tparam ES1 The proposed subset
2657 /// @tparam ES2 The proposed superset
2658 template <typename ES1, typename ES2>
2659 static inline constexpr bool is_subset_of_v = is_subset_of<ES1, ES2>::value;
2660 
2661 /// @relates error_set
2662 /// @class is_equivalent error_set.hpp <sumty/error_set.hpp>
2663 /// @brief Utility to test that one @ref error_set is equivalent to another.
2664 ///
2665 /// @details
2666 /// This type is a template metaprogramming utility to test that the @ref
2667 /// error_set `ES1` is equivalent to the @ref error_set `ES2`. Two @ref
2668 /// error_set instantiations are equivalent if each alternative type of
2669 /// one is an alternative type in the other, in both directions. Thus
2670 /// equivalence is commutative. So, reworded, the two are equivalent if
2671 /// the have the same alternative types, but potentially in different
2672 /// orders.
2673 ///
2674 /// @ref is_equivalent has the member constant `value`, which is `true`
2675 /// if the two @ref error_set instantiations are equivalent, and `false`
2676 /// otherwise.
2677 ///
2678 /// ## Example
2679 /// ```
2680 /// assert(is_equivalent<
2681 /// error_set<error1, error2, error3>,
2682 /// error_set<error2, error1, error3>
2683 /// >::value);
2684 ///
2685 /// assert(!is_equivalent<
2686 /// error_set<error1, error2, error3>,
2687 /// error_set<error1, error2>
2688 /// >::value);
2689 /// ```
2690 ///
2691 /// @tparam ES1 An @ref error_set type
2692 /// @tparam ES2 An @ref error_set type
2693 template <typename ES1, typename ES2>
2694 struct is_equivalent : detail::is_equivalent_impl<ES1, ES2> {};
2695 
2696 /// @relates is_equivalent
2697 /// @brief Utility to test that one @ref error_set is equivalent to another.
2698 ///
2699 /// @details
2700 /// This is a template metaprogramming utility to test that the @ref
2701 /// error_set `ES1` is equivalent to the @ref error_set `ES2`. Two @ref
2702 /// error_set instantiations are equivalent if each alternative type of
2703 /// one is an alternative type in the other, in both directions. Thus
2704 /// equivalence is commutative. So, reworded, the two are equivalent if
2705 /// the have the same alternative types, but potentially in different
2706 /// orders.
2707 ///
2708 /// ## Example
2709 /// ```
2710 /// assert(is_equivalent_v<
2711 /// error_set<error1, error2, error3>,
2712 /// error_set<error2, error1, error3>
2713 /// >);
2714 ///
2715 /// assert(!is_equivalent_v<
2716 /// error_set<error1, error2, error3>,
2717 /// error_set<error1, error2>
2718 /// >);
2719 /// ```
2720 ///
2721 /// @tparam ES1 An @ref error_set type
2722 /// @tparam ES2 An @ref error_set type
2723 template <typename ES1, typename ES2>
2724 static inline constexpr bool is_equivalent_v = is_equivalent<ES1, ES2>::value;
2725 
2726 /// @relates error_set
2727 /// @class error_set_size error_set.hpp <sumty/error_set.hpp>
2728 /// @brief Utility to get the number of alternatives in an @ref error_set
2729 ///
2730 /// @details
2731 /// @ref error_set_size provides the number alternatives in the @ref error_set,
2732 /// `T`, in the static constexpr member `value`.
2733 ///
2734 /// ## Example
2735 /// ```cpp
2736 /// assert(error_set_size<error_set<error1, error2, error3>>::value == 3);
2737 /// ```
2738 ///
2739 /// @tparam T The @ref error_set type to get the size of
2740 template <typename T>
2741 struct error_set_size : detail::error_set_size_helper<T> {};
2742 
2743 /// @relates error_set_size
2744 /// @brief Utility to get the number of alternatives in an @ref error_set
2745 ///
2746 /// @details
2747 /// ## Example
2748 /// ```cpp
2749 /// assert(error_set_size_v<error_set<error1, error2, error3>> == 3);
2750 /// ```
2751 ///
2752 /// @tparam T The @ref error_set type to get the size of
2753 template <typename T>
2754 static inline constexpr size_t error_set_size_v = error_set_size<T>::value;
2755 
2756 template <typename... T>
2757 struct variant_size<error_set<T...>> : error_set_size<error_set<T...>> {};
2758 
2759 template <typename... T>
2760 struct variant_size<const error_set<T...>> : error_set_size<const error_set<T...>> {};
2761 
2762 /// @relates error_set
2763 /// @class error_set_alternative error_set.hpp <sumty/error_set.hpp>
2764 /// @brief Utility to get the type of a @ref error_set alternative with some index
2765 ///
2766 /// @details
2767 /// @ref error_set_alternative provides the type of an alternative with the
2768 /// index `I` in the member type, `type`.
2769 ///
2770 /// ## Example
2771 /// ```cpp
2772 /// assert(std::is_same_v<
2773 /// typename error_set_alternative<1, variant<error1, error2, error3>>::type,
2774 /// error2
2775 /// >);
2776 /// ```
2777 ///
2778 /// @tparam I The index of the @ref error_set alternative
2779 ///
2780 /// @tparam ES The @ref error_set type containing the alternative
2781 template <size_t I, typename ES>
2782 struct error_set_alternative : detail::error_set_alternative_helper<I, ES> {};
2783 
2784 /// @relates error_set_alternative
2785 /// @brief Utility to get the type of a @ref error_set alternative with some index
2786 ///
2787 /// @details
2788 /// @ref error_set_alternative_t is the type of an alternative with the index `I`.
2789 ///
2790 /// ## Example
2791 /// ```cpp
2792 /// assert(std::is_same_v<
2793 /// error_set_alternative_t<1, variant<error1, error2, error3>>,
2794 /// error2
2795 /// >);
2796 /// ```
2797 ///
2798 /// @tparam I The index of the @ref error_set alternative
2799 ///
2800 /// @tparam ES The @ref error_set type containing the alternative
2801 template <size_t I, typename ES>
2802 using error_set_alternative_t = typename error_set_alternative<I, ES>::type;
2803 
2804 template <size_t I, typename... T>
2805 struct variant_alternative<I, error_set<T...>> : error_set_alternative<I, error_set<T...>> {
2806 };
2807 
2808 template <size_t I, typename... T>
2809 struct variant_alternative<I, const error_set<T...>>
2810  : error_set_alternative<I, const error_set<T...>> {};
2811 
2812 /// @relates error_set
2813 /// @class merge_error_set error_set.hpp <sumty/error_set.hpp>
2814 /// @brief Utility to merge two @ref error_set types
2815 ///
2816 /// @details
2817 /// @ref merge_error_set combines the alternative types of two @ref error_set
2818 /// instantiations into a single @ref error_set instantiation. The two types
2819 /// may or may not have alternatives in common. Since @ref error_set requires
2820 /// that there are no two alternatives with the same type, any alternatives
2821 /// common between the two @ref error_set instantiations are deduplicated.
2822 ///
2823 /// ## Example
2824 /// ```
2825 /// using merged = typename merge_error_set<
2826 /// error_set<error1, error2>,
2827 /// error_set<error3, error1>
2828 /// >::type;
2829 ///
2830 /// assert(is_equivalent_v<merged, error_set<error1, error2, error3>>);
2831 /// ```
2832 ///
2833 /// @tparam ES1 An @ref error_set type
2834 /// @tparam ES2 An @ref error_set type
2835 template <typename ES1, typename ES2>
2836 struct merge_error_set : detail::merge_error_set_helper<ES1, ES2> {};
2837 
2838 /// @relates merge_error_set
2839 /// @brief Utility to merge two @ref error_set types
2840 ///
2841 /// @details
2842 /// @ref merge_error_set combines the alternative types of two @ref error_set
2843 /// instantiations into a single @ref error_set instantiation. The two types
2844 /// may or may not have alternatives in common. Since @ref error_set requires
2845 /// that there are no two alternatives with the same type, any alternatives
2846 /// common between the two @ref error_set instantiations are deduplicated.
2847 ///
2848 /// ## Example
2849 /// ```
2850 /// using merged = merge_error_set_t<
2851 /// error_set<error1, error2>,
2852 /// error_set<error3, error1>
2853 /// >;
2854 ///
2855 /// assert(is_equivalent_v<merged, error_set<error1, error2, error3>>);
2856 /// ```
2857 ///
2858 /// @tparam ES1 An @ref error_set type
2859 /// @tparam ES2 An @ref error_set type
2860 template <typename ES1, typename ES2>
2861 using merge_error_set_t = typename merge_error_set<ES1, ES2>::type;
2862 
2863 /// @relates error_set
2864 /// @class make_error_set error_set.hpp <sumty/error_set.hpp>
2865 /// @brief Utility to create an @ref error_set type.
2866 ///
2867 /// @details
2868 /// @ref make_error_set is a builder for the @ref error_set type that
2869 /// deduplicates alternatives. @ref error_set requires that each alternative
2870 /// be a unique type, so @ref make_error_set simplifies generically building
2871 /// an @ref error_set by ensuring that condition is met automatically.
2872 ///
2873 /// ## Example
2874 /// ```
2875 /// assert(is_equivalent_v<
2876 /// typename make_error_set<
2877 /// error1, error2, error1, error1, error3, error2
2878 /// >::type,
2879 /// error_set<error1, error2, error3>
2880 /// >);
2881 /// ```
2882 ///
2883 /// @tparam T The alternative types of the resulting @ref error_set
2884 template <typename... T>
2885 struct make_error_set : detail::make_error_set_helper<T...> {};
2886 
2887 /// @relates make_error_set
2888 /// @brief Utility to create an @ref error_set type.
2889 ///
2890 /// @details
2891 /// @ref make_error_set is a builder for the @ref error_set type that
2892 /// deduplicates alternatives. @ref error_set requires that each alternative
2893 /// be a unique type, so @ref make_error_set simplifies generically building
2894 /// an @ref error_set by ensuring that condition is met automatically.
2895 ///
2896 /// ## Example
2897 /// ```
2898 /// assert(is_equivalent_v<
2899 /// make_error_set_t<error1, error2, error1, error1, error3, error2>,
2900 /// error_set<error1, error2, error3>
2901 /// >);
2902 /// ```
2903 ///
2904 /// @tparam T The alternative types of the resulting @ref error_set
2905 template <typename... T>
2906 using make_error_set_t = typename make_error_set<T...>::type;
2907 
2908 /// @private
2909 template <typename... T>
2910  requires((true && ... && std::is_base_of_v<never, std::remove_cvref_t<T>>))
2911 class error_set<T...> : public never {};
2912 
2913 } // namespace sumty
2914 
2915 #endif