plugify 1.0.0.0
Loading...
Searching...
No Matches
variant.hpp
1#pragma once
2
3#include <exception>
4#include <type_traits>
5#include <utility> // swap
6#include <limits> // used for index_type
7#include <initializer_list>
8#include <cassert>
9
10#ifndef PLUGIFY_VARIANT_NO_CONSTEXPR_EMPLACE
11# include <memory>
12#else
13# include <new>
14#endif
15
16#ifndef PLUGIFY_VARIANT_NO_STD_HASH
17# include <functional>
18#endif
19
20#define PLG_FWD(x) static_cast<decltype(x)&&>(x)
21#define PLG_MOV(x) static_cast< std::remove_reference_t<decltype(x)>&& >(x)
22
23#include "macro.hpp"
24
25// from https://github.com/groundswellaudio/swl-variant
26namespace plg {
27#if PLUGIFY_EXCEPTIONS
28 class bad_variant_access final : public std::exception {
29 const char* message = ""; // llvm test requires a well formed what() on default init
30 public :
31 explicit bad_variant_access(const char* str) noexcept : message{str} {}
32 bad_variant_access() noexcept = default;
33 bad_variant_access(const bad_variant_access&) noexcept = default;
34 bad_variant_access& operator=(const bad_variant_access&) noexcept = default;
35 const char* what() const noexcept override { return message; }
36 };
37#endif // PLUGIFY_EXCEPTIONS
38
39 namespace detail {
40 struct variant_tag{};
41 struct emplacer_tag{};
42 }
43
44 template<class T>
46
47 template<std::size_t Index>
49
50 template<std::size_t Index>
51 inline static constexpr in_place_index_t<Index> in_place_index;
52
53 template<class T>
54 inline static constexpr in_place_type_t<T> in_place_type;
55
56 namespace detail {
57 template<int N>
58 constexpr int find_first_true(bool (&&arr)[N]) {
59 for (int k = 0; k < N; ++k)
60 if (arr[k])
61 return k;
62 return -1;
63 }
64
65 template<class T, class... Ts>
66 inline constexpr bool appears_exactly_once = (static_cast<unsigned short>(std::is_same_v<T, Ts>) + ...) == 1;
67
68 // ============= type pack element
69
70#if __has_builtin(__type_pack_element)
71 template<std::size_t K, class... Ts>
72 using type_pack_element = __type_pack_element<K, Ts...>;
73#else
74 template<unsigned char = 1>
76
77 template<>
78 struct find_type_i<1> {
79 template<std::size_t Idx, class T, class... Ts>
80 using f = typename find_type_i<(Idx != 1)>::template f<Idx - 1, Ts...>;
81 };
82
83 template<>
84 struct find_type_i<0> {
85 template<std::size_t, class T, class... Ts>
86 using f = T;
87 };
88
89 template<std::size_t K, class... Ts>
90 using type_pack_element = typename find_type_i<(K != 0)>::template f<K, Ts...>;
91#endif
92
93 // ============= overload match detector. to be used for variant generic assignment
94
95 template<class T>
96 using arr1 = T[1];
97
98 template<std::size_t N, class A>
100 using type = A;
101 template<class T>
102#if __cpp_concepts
103 requires requires { arr1<A>{std::declval<T>()}; }
104#endif // __cpp_concepts
105 auto operator()(A, T&&) -> overload_frag<N, A>;
106 };
107
108 template<class Seq, class... Args>
110
111 template<std::size_t... Idx, class... Args>
112 struct make_overload<std::integer_sequence<std::size_t, Idx...>, Args...>
113 : overload_frag<Idx, Args>... {
114 using overload_frag<Idx, Args>::operator()...;
115 };
116
117 template<class T, class... Ts>
118 using best_overload_match = typename decltype(
119 make_overload<std::make_index_sequence<sizeof...(Ts)>, Ts...>{}
120 (std::declval<T>(), std::declval<T>())
121 )::type;
122
123#if __cpp_concepts
124 template<class T, class... Ts>
126 requires { typename best_overload_match<T, Ts...>; };
127
128 // ================================== rel ops
129
130 template<class From, class To>
131 concept convertible = std::is_convertible_v<From, To>;
132
133 template<class T>
134 concept has_eq_comp = requires (T a, T b) {
135 { a == b } -> convertible<bool>;
136 };
137
138 template<class T>
139 concept has_lesser_comp = requires (T a, T b) {
140 { a < b } -> convertible<bool>;
141 };
142
143 template<class T>
144 concept has_less_or_eq_comp = requires (T a, T b) {
145 { a <= b } -> convertible<bool>;
146 };
147#endif // __cpp_concepts
148
149 template<class A>
151 template<class T, class NT, NT N>
152 constexpr void operator()(T&& elem, std::integral_constant<NT, N> index_) const {
153 a.template emplace_no_dtor<index_>(static_cast<T&&>(elem));
154 }
155 A& a;
156 };
157
158 template<class E, class T>
159 constexpr void destruct(T& obj) {
160 if constexpr (not std::is_trivially_destructible_v<E>)
161 obj.~E();
162 }
163
164 // =============================== variant union types
165
166 // =================== base variant storage type
167 // this type is used to build a N-ary tree of union.
168
169 struct dummy_type{ static constexpr int elem_size = 0; }; // used to fill the back of union nodes
170
171 using union_index_t = unsigned;
172
173#define TRAIT(trait) (std::is_##trait##_v<A> && std::is_##trait##_v<B>)
174
175#ifdef __cpp_concepts
176# define SFM_DEF_REQ(trait) \
177 requires (TRAIT(trait) and not TRAIT(trivially_##trait))
178#else // !__cpp_concepts
179# define SFM_DEF_REQ(trait)
180#endif // __cpp_concepts
181
182#define SFM(signature, impl) \
183 signature \
184 impl \
185
186#define SFM2(signature, req, impl) \
187 signature = default; \
188 signature \
189 req \
190 impl
191
192#if __cpp_constexpr >= 201907L
193# define SFM_DTOR(X) constexpr ~X()
194#else // __cpp_constexpr < 201907L
195# define SFM_DTOR(X) ~X()
196#endif
197
198// given the two members of type A and B of an union X
199// this create the proper conditionally trivial special members functions
200#ifdef __cpp_concepts
201# define INJECT_UNION_SFM(X) \
202 SFM2(constexpr X (const X &), SFM_DEF_REQ(copy_constructible), {}) \
203 SFM2(constexpr X (X&&) noexcept, SFM_DEF_REQ(move_constructible), {}) \
204 SFM2(constexpr X& operator=(const X&), SFM_DEF_REQ(copy_assignable), { return *this; }) \
205 SFM2(constexpr X& operator=(X&&) noexcept, SFM_DEF_REQ(move_assignable), { return *this; }) \
206 SFM2(SFM_DTOR(X), SFM_DEF_REQ(destructible), {})
207# else // !__cpp_concepts
208# define INJECT_UNION_SFM(X) \
209 SFM(constexpr X (const X &), {}) \
210 SFM(constexpr X (X&&) noexcept, {}) \
211 SFM(constexpr X& operator=(const X&), { return *this; }) \
212 SFM(constexpr X& operator=(X&&) noexcept, { return *this; }) \
213 SFM(SFM_DTOR(X), {})
214#endif // __cpp_concepts
215
216 template<bool IsLeaf>
218
219 template<>
220 struct node_trait<true> {
221
222 template<class A, class B>
223 static constexpr auto elem_size = not(std::is_same_v<B, dummy_type>) ? 2 : 1;
224
225 template<std::size_t, class>
226 static constexpr char ctor_branch = 0;
227 };
228
229 template<>
231 template<class A, class B>
232 static constexpr auto elem_size = A::elem_size + B::elem_size;
233
234 template<std::size_t Index, class A>
235 static constexpr char ctor_branch = (Index < A::elem_size) ? 1 : 2;
236 };
237
238 template<bool IsLeaf, class A, class B>
239 struct union_node {
240 union {
241 A a;
242 B b;
243 };
244
245 static constexpr auto elem_size = node_trait<IsLeaf>::template elem_size<A, B>;
246
247 constexpr union_node() = default;
248
249 template<std::size_t Index, class... Args>
250#ifdef __cpp_concepts
252#endif // __cpp_concepts
253 constexpr explicit union_node(in_place_index_t<Index>, Args&&... args)
254 : a{in_place_index<Index>, static_cast<Args&&>(args)...}
255 {}
256
257#ifdef __cpp_concepts
258 template<std::size_t Index, class... Args>
260 constexpr explicit union_node(in_place_index_t<Index>, Args&&... args)
261 : b{in_place_index<Index - A::elem_size>, static_cast<Args&&>(args)...}
262 {}
263#endif // __cpp_concepts
264
265 template<class... Args>
266#ifdef __cpp_concepts
267 requires (IsLeaf)
268#endif // __cpp_concepts
269 constexpr explicit union_node(in_place_index_t<0>, Args&&... args)
270 : a{static_cast<Args&&>(args)...}
271 {}
272
273 template<class... Args>
274#ifdef __cpp_concepts
275 requires (IsLeaf)
276#endif // __cpp_concepts
277 constexpr explicit union_node(in_place_index_t<1>, Args&&... args)
278 : b{static_cast<Args&&>(args)...}
279 {}
280
281 constexpr explicit union_node(dummy_type)
282#ifdef __cpp_concepts
283 requires (std::is_same_v<dummy_type, B>)
284#endif // __cpp_concepts
285 : b{}
286 {}
287
288 template<union_index_t Index>
289 constexpr auto& get() {
290 if constexpr (IsLeaf) {
291 if constexpr (Index == 0)
292 return a;
293 else
294 return b;
295 } else {
296 if constexpr (Index < A::elem_size)
297 return a.template get<Index>();
298 else
299 return b.template get<Index - A::elem_size>();
300 }
301 }
302
303 template<union_index_t Index>
304 constexpr const auto& get() const {
305 return const_cast<union_node&>(*this).get<Index>();
306 }
307
308 INJECT_UNION_SFM(union_node)
309 };
310
311#undef INJECT_UNION_SFM
312#undef SFM
313#undef TRAIT
314
315 // =================== algorithm to build the tree of unions
316 // take a sequence of types and perform an order preserving fold until only one type is left
317 // the first parameter is the numbers of types remaining for the current pass
318
319 constexpr unsigned char pick_next(unsigned remaining) {
320 return static_cast<unsigned char>(remaining >= 2 ? 2 : remaining);
321 }
322
323 template<unsigned char Pick, unsigned char GoOn, bool FirstPass>
324 struct make_tree;
325
326 template<bool IsFirstPass>
327 struct make_tree<2, 1, IsFirstPass> {
328 template<unsigned Remaining, class A, class B, class... Ts>
329 using f = typename make_tree
330 <
331 pick_next(Remaining - 2),
332 sizeof...(Ts) != 0,
334 >
335 ::template f
336 <
337 Remaining - 2,
338 Ts...,
340 >;
341 };
342
343 // only one type left, stop
344 template<bool F>
345 struct make_tree<0, 0, F> {
346 template<unsigned, class A>
347 using f = A;
348 };
349
350 // end of one pass, restart
351 template<bool IsFirstPass>
352 struct make_tree<0, 1, IsFirstPass> {
353 template<unsigned Remaining, class... Ts>
354 using f = typename make_tree
355 <
356 pick_next(sizeof...(Ts)),
357 (sizeof...(Ts) != 1),
358 false // <- both first pass and tail call recurse into a tail call
359 >
360 ::template f<sizeof...(Ts), Ts...>;
361 };
362
363 // one odd type left in the pass, put it at the back to preserve the order
364 template<>
365 struct make_tree<1, 1, false> {
366 template<unsigned Remaining, class A, class... Ts>
367 using f = typename make_tree<0, sizeof...(Ts) != 0, false>
368 ::template f<0, Ts..., A>;
369 };
370
371 // one odd type left in the first pass, wrap it in an union
372 template<>
373 struct make_tree<1, 1, true> {
374 template<unsigned, class A, class... Ts>
375 using f = typename make_tree<0, sizeof...(Ts) != 0, false>
376 ::template f<0, Ts..., union_node<true, A, dummy_type>>;
377 };
378
379 template<class... Ts>
380 using make_tree_union = typename
381 make_tree<pick_next(sizeof...(Ts)), 1, true>::template f<sizeof...(Ts), Ts...>;
382
383 // ============================================================
384
385 // Ts... must be sorted in ascending size
386 template<std::size_t Num, class... Ts>
388 type_pack_element<(static_cast<unsigned char>(Num > std::numeric_limits<Ts>::max()) + ...),
389 Ts...
390 >;
391
392 // why do we need this again? i think something to do with GCC?
393 namespace swap_trait {
394 using std::swap;
395
396#ifdef __cpp_concepts
397 template<class A>
398 concept able = requires (A a, A b) { swap(a, b); };
399#endif // __cpp_concepts
400
401 template<class A>
402 inline constexpr bool nothrow = noexcept(swap(std::declval<A&>(), std::declval<A&>()));
403 }
404
405#ifndef PLUGIFY_VARIANT_NO_STD_HASH
406# ifdef __cpp_concepts
407 template<class T>
408 inline constexpr bool has_std_hash =
409 requires (T t)
410 {
411 std::size_t(::std::hash<std::remove_cvref_t<T>>{}(t));
412 };
413# endif // __cpp_concepts
414#endif // PLUGIFY_VARIANT_NO_STD_HASH
415
416 template<class T>
417 inline constexpr T* addressof(T& obj) noexcept {
418#if defined(__GNUC__) || defined(__clang__)
419 return __builtin_addressof(obj);
420#elif defined(PLUGIFY_VARIANT_NO_CONSTEXPR_EMPLACE)
421 // if & is overloaded, use the ugly version
422 if constexpr (requires { obj.operator&(); })
423 return reinterpret_cast<T*>
424 (&const_cast<char&>(reinterpret_cast<const volatile char&>(obj)));
425 else
426 return &obj;
427#else
428 return std::addressof(obj);
429#endif
430 }
431
432 // ========================= visit dispatcher
433
434 template<class Fn, class... Vars>
435 using rtype_visit = decltype((std::declval<Fn>()(std::declval<Vars>().template unsafe_get<0>()...)));
436
437 template<class Fn, class Var>
438 using rtype_index_visit = decltype((std::declval<Fn>()(std::declval<Var>().template unsafe_get<0>(),
439 std::integral_constant<std::size_t, 0>{}))
440 );
441
442 inline namespace v1 {
443
444#define DEC(N) X((N)) X((N) + 1) X((N) + 2) X((N) + 3) X((N) + 4) X((N) + 5) X((N) + 6) X((N) + 7) X((N) + 8) X((N) + 9)
445
446#define SEQ30(N) DEC((N) + 0) DEC((N) + 10) DEC((N) + 20)
447#define SEQ100(N) SEQ30((N) + 0) SEQ30((N) + 30) SEQ30((N) + 60) DEC((N) + 90)
448#define SEQ200(N) SEQ100((N) + 0) SEQ100((N) + 100)
449#define SEQ400(N) SEQ200((N) + 0) SEQ200((N) + 200)
450#define CAT(M, N) M##N
451#define CAT2(M, N) CAT(M, N)
452#define INJECTSEQ(N) CAT2(SEQ, N)(0)
453
454 // single-visitation
455
456 template<unsigned Offset, class Rtype, class Fn, class V>
457 constexpr Rtype single_visit_tail(Fn&& fn, V&& v) {
458
459 constexpr auto RemainingIndex = std::decay_t<V>::size - Offset;
460
461#define X(N) case (N + Offset) : \
462 if constexpr (N < RemainingIndex) { \
463 return static_cast<Fn&&>(fn)(static_cast<V&&>(v).template unsafe_get<N+Offset>()); \
464 break; \
465 } else PLUGIFY_UNREACHABLE();
466
467#define SEQSIZE 100
468
469 switch (v.index()) {
470
471 INJECTSEQ(SEQSIZE)
472
473 default:
474 if constexpr (SEQSIZE < RemainingIndex)
475 return detail::single_visit_tail<Offset + SEQSIZE, Rtype>(static_cast<Fn&&>(fn), static_cast<V&&>(v));
476 else
477 PLUGIFY_UNREACHABLE();
478 }
479
480#undef X
481#undef SEQSIZE
482 }
483
484 template<unsigned Offset, class Rtype, class Fn, class V>
485 constexpr Rtype single_visit_w_index_tail(Fn&& fn, V&& v) {
486
487 constexpr auto RemainingIndex = std::decay_t<V>::size - Offset;
488
489#define X(N) case (N + Offset) : \
490 if constexpr (N < RemainingIndex) { \
491 return static_cast<Fn&&>(fn)(static_cast<V&&>(v).template unsafe_get<N+Offset>(), std::integral_constant<unsigned, N+Offset>{}); \
492 break; \
493 } else PLUGIFY_UNREACHABLE();
494
495#define SEQSIZE 100
496
497 switch (v.index()) {
498
499 INJECTSEQ(SEQSIZE)
500
501 default:
502 if constexpr (SEQSIZE < RemainingIndex)
503 return detail::single_visit_w_index_tail<Offset + SEQSIZE, Rtype>(static_cast<Fn&&>(fn), static_cast<V&&>(v));
504 else
505 PLUGIFY_UNREACHABLE();
506 }
507
508#undef X
509#undef SEQSIZE
510 }
511
512 template<class Fn, class V>
513 constexpr decltype(auto) visit(Fn&& fn, V&& v) {
514 return detail::single_visit_tail<0, rtype_visit<Fn&&, V&&>>(PLG_FWD(fn), PLG_FWD(v));
515 }
516
517 // unlike other visit functions, this takes the variant first!
518 // this is confusing, but make the client code easier to read
519 template<class Fn, class V>
520 constexpr decltype(auto) visit_with_index(V&& v, Fn&& fn) {
521 return detail::single_visit_w_index_tail<0, rtype_index_visit<Fn&&, V&&>>(PLG_FWD(fn), PLG_FWD(v));
522 }
523
524 template<class Fn, class Head, class... Tail>
525 constexpr decltype(auto) multi_visit(Fn&& fn, Head&& head, Tail&&... tail) {
526
527 // visit them one by one, starting with the last
528 auto vis = [&fn, &head] (auto&&... args) -> decltype(auto) {
529 return detail::visit([&fn, &args...] (auto&& elem) -> decltype(auto) {
530 return PLG_FWD(fn)(PLG_FWD(elem), PLG_FWD(args)...);
531 }, PLG_FWD(head));
532 };
533
534 if constexpr (sizeof...(tail) == 0)
535 return PLG_FWD(vis)();
536 else if constexpr (sizeof...(tail) == 1)
537 return detail::visit(PLG_FWD(vis), PLG_FWD(tail)...);
538 else
539 return detail::multi_visit(PLG_FWD(vis), PLG_FWD(tail)...);
540 }
541
542#undef DEC
543#undef SEQ30
544#undef SEQ100
545#undef SEQ200
546#undef SEQ400
547#undef CAT
548#undef CAT2
549#undef INJECTSEQ
550
551 } // inline namespace v1
552
554 template<class T>
555 constexpr bool operator==(T idx) const noexcept { return idx == std::numeric_limits<T>::max(); }
556 };
557 }
558
559 inline static constexpr detail::variant_npos_t variant_npos;
560
561 template<class... Ts>
562 class variant;
563
564 template<typename T>
565 struct is_variant_impl : std::false_type {};
566
567 template<typename... Ts>
568 struct is_variant_impl<variant<Ts...>> : std::true_type {};
569
570 template<typename T>
571 struct is_variant : is_variant_impl<std::decay_t<T>> {};
572
573 template<typename T>
574 inline constexpr bool is_variant_v = is_variant<T>::value;
575
576 // ill-formed variant, an empty specialization prevents some really bad errors messages on gcc
577#ifdef __cpp_concepts
578 template<class... Ts>
579 requires (
580 (std::is_array_v<Ts> || ...)
581 || (std::is_reference_v<Ts> || ...)
582 || (std::is_void_v<Ts> || ...)
583 || sizeof...(Ts) == 0
584 )
585 class variant<Ts...> {
586 static_assert(sizeof...(Ts) > 0, "A variant cannot be empty.");
587 static_assert(not(std::is_reference_v<Ts> || ...), "A variant cannot contain references, consider using reference wrappers instead.");
588 static_assert(not(std::is_void_v<Ts> || ...), "A variant cannot contains void.");
589 static_assert(not(std::is_array_v<Ts> || ...), "A variant cannot contain a raw array type, consider using std::array instead.");
590 };
591#endif // __cpp_concepts
592
593 template<class... Ts>
594 class variant {
595 using storage = detail::union_node<false, detail::make_tree_union<Ts...>, detail::dummy_type>;
596
597 static constexpr bool is_trivial = std::is_trivial_v<storage>;
598 static constexpr bool has_copy_ctor = std::is_copy_constructible_v<storage>;
599 static constexpr bool trivial_copy_ctor = is_trivial || std::is_trivially_copy_constructible_v<storage>;
600 static constexpr bool has_copy_assign = std::is_copy_constructible_v<storage>;
601 static constexpr bool trivial_copy_assign = is_trivial || std::is_trivially_copy_assignable_v<storage>;
602 static constexpr bool has_move_ctor = std::is_move_constructible_v<storage>;
603 static constexpr bool trivial_move_ctor = is_trivial || std::is_trivially_move_constructible_v<storage>;
604 static constexpr bool has_move_assign = std::is_move_assignable_v<storage>;
605 static constexpr bool trivial_move_assign = is_trivial || std::is_trivially_move_assignable_v<storage>;
606 static constexpr bool trivial_dtor = std::is_trivially_destructible_v<storage>;
607
608 public:
609 template<std::size_t Idx>
610 using alternative = std::remove_reference_t<decltype(std::declval<storage&>().template get<Idx>())>;
611
612 static constexpr bool can_be_valueless = not is_trivial;
613
614 static constexpr unsigned size = sizeof...(Ts);
615
616 using index_type = detail::smallest_suitable_integer_type<sizeof...(Ts) + can_be_valueless, unsigned char, unsigned short, unsigned>;
617
618 static constexpr index_type npos = static_cast<index_type>(-1);
619
620 template<class T>
621 static constexpr int index_of = detail::find_first_true({std::is_same_v<T, Ts>...});
622
623 // ============================================= constructors (20.7.3.2)
624
625 // default constructor
626 constexpr variant()
627 noexcept(std::is_nothrow_default_constructible_v<alternative<0>>)
629 requires std::is_default_constructible_v<alternative<0>>
630#endif // __cpp_concepts
631 : _storage{in_place_index<0>}, _current{0}
632 {}
633
634 // copy constructor (trivial)
635#ifdef __cpp_concepts
636 constexpr variant(const variant&)
637 requires trivial_copy_ctor
638 = default;
639#endif // __cpp_concepts
640
641 // note : both the copy and move constructor cannot be meaningfully constexpr without std::construct_at
642 // copy constructor
643 constexpr variant(const variant& o)
644#ifdef __cpp_concepts
645 requires (has_copy_ctor and not trivial_copy_ctor)
646#endif // __cpp_concepts
647 : _storage{detail::dummy_type{}} {
648 construct_from(o);
649 }
650
651 // move constructor (trivial)
652#ifdef __cpp_concepts
653 constexpr variant(variant&&)
654 requires trivial_move_ctor
655 = default;
656#endif // __cpp_concepts
657
658 // move constructor
659 constexpr variant(variant&& o)
660 noexcept((std::is_nothrow_move_constructible_v<Ts> && ...))
662 requires (has_move_ctor and not trivial_move_ctor)
663#endif // __cpp_concepts
664 : _storage{detail::dummy_type{}} {
665 construct_from(static_cast<variant&&>(o));
666 }
667
668 // generic constructor
669 template<class T, class M = detail::best_overload_match<T&&, Ts...>, class D = std::decay_t<T>>
670 constexpr variant(T&& t)
671 noexcept(std::is_nothrow_constructible_v<M, T&&>)
673 requires (not std::is_same_v<D, variant> and not std::is_base_of_v<detail::emplacer_tag, D>)
674#endif // __cpp_concepts
675 : variant{in_place_index<index_of<M>>, static_cast<T&&>(t)}
676 {}
677
678 // construct at index
679 template<std::size_t Index, class... Args>
680#ifdef __cpp_concepts
682#endif // __cpp_concepts
683 explicit constexpr variant(in_place_index_t<Index> tag, Args&&... args)
684 : _storage{tag, static_cast<Args&&>(args)...}, _current(Index)
685 {}
686
687 // construct a given type
688 template<class T, class... Args>
689#ifdef __cpp_concepts
690 requires (detail::appears_exactly_once<T, Ts...> && std::is_constructible_v<T, Args&&...>)
691#endif // __cpp_concepts
692 explicit constexpr variant(in_place_type_t<T>, Args&&... args)
693 : variant{in_place_index<index_of<T>>, static_cast<Args&&>(args)...}
694 {}
695
696 // initializer-list constructors
697 template<std::size_t Index, class U, class... Args>
698#ifdef __cpp_concepts
699 requires (
700 (Index < size) and
701 std::is_constructible_v<alternative<Index>, std::initializer_list<U>&, Args&&...>
702 )
703#endif // __cpp_concepts
704 explicit constexpr variant(in_place_index_t<Index> tag, std::initializer_list<U> list, Args&&... args)
705 : _storage{tag, list, PLG_FWD(args)...}, _current{Index}
706 {}
707
708 template<class T, class U, class... Args>
709#ifdef __cpp_concepts
710 requires (
711 detail::appears_exactly_once<T, Ts...>
712 && std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>
713 )
714#endif // __cpp_concepts
715 explicit constexpr variant(in_place_type_t<T>, std::initializer_list<U> list, Args&&... args)
716 : _storage{in_place_index<index_of<T>>, list, PLG_FWD(args)...}, _current{index_of<T> }
717 {}
718
719 // ================================ destructors (20.7.3.3)
720
721#ifdef __cpp_concepts
722# if __cpp_constexpr >= 201907L
723 constexpr ~variant() requires trivial_dtor = default;
724 constexpr ~variant() requires (not trivial_dtor) {
725 reset();
726 }
727# else // __cpp_constexpr < 201907L
728 ~variant() requires trivial_dtor = default;
729 ~variant() requires (not trivial_dtor) {
730 reset();
731 }
732# endif // __cpp_constexpr >= 201907L
733#else // !__cpp_concepts
734 ~variant() {
735 reset();
736 }
737#endif // __cpp_concepts
738
739 // ================================ assignment(20.7.3.4)
740
741 // copy assignment(trivial)
742#ifdef __cpp_concepts
743 constexpr variant& operator=(const variant& o)
744 requires trivial_copy_assign && trivial_copy_ctor
745 = default;
746#endif // __cpp_concepts
747
748 // copy assignment
749 constexpr variant& operator=(const variant& rhs)
750#ifdef __cpp_concepts
751 requires (has_copy_assign and not(trivial_copy_assign && trivial_copy_ctor))
752#endif // __cpp_concepts
753 {
754 if (this == &rhs) [[unlikely]]
755 return *this;
756
757 assign_from(rhs, [&](const auto& elem, auto index_cst) {
758 if (index() == index_cst)
760 else {
761 using type = alternative<index_cst>;
762 constexpr bool do_simple_copy =
763 std::is_nothrow_copy_constructible_v<type>
764 or not std::is_nothrow_move_constructible_v<type>;
765 if constexpr (do_simple_copy)
767 else {
769 emplace<index_cst>(PLG_MOV(tmp));
770 }
771 }
772 });
773 return *this;
774 }
775
776 // move assignment(trivial)
777#ifdef __cpp_concepts
778 constexpr variant& operator=(variant&& o)
779 requires (trivial_move_assign and trivial_move_ctor and trivial_dtor)
780 = default;
781#endif // __cpp_concepts
782
783 // move assignment
784 constexpr variant& operator=(variant&& o)
785 noexcept((std::is_nothrow_move_constructible_v<Ts> && ...) && (std::is_nothrow_move_assignable_v<Ts> && ...))
787 requires (has_move_assign && has_move_ctor and not(trivial_move_assign and trivial_move_ctor and trivial_dtor))
788#endif // __cpp_concepts
789 {
790 if (this == &o) [[unlikely]]
791 return *this;
792
793 assign_from(PLG_FWD(o), [&](auto&& elem, auto index_cst) {
794 if (index() == index_cst)
795 unsafe_get<index_cst>() = PLG_MOV(elem);
796 else
797 emplace<index_cst>(PLG_MOV(elem));
798 });
799 return *this;
800 }
801
802 // generic assignment
803 template<class T>
804#ifdef __cpp_concepts
805 requires detail::has_non_ambiguous_match<T, Ts...>
806#endif // __cpp_concepts
807 constexpr variant& operator=(T&& t)
808 noexcept(std::is_nothrow_assignable_v<detail::best_overload_match<T&&, Ts...>, T&&>
809 && std::is_nothrow_constructible_v<detail::best_overload_match<T&&, Ts...>, T&&>) {
810 using related_type = detail::best_overload_match<T&&, Ts...>;
811 constexpr auto new_index = index_of<related_type>;
812
813 if (_current == new_index)
814 unsafe_get<new_index>() = PLG_FWD(t);
815 else {
816 constexpr bool do_simple_emplace =
817 std::is_nothrow_constructible_v<related_type, T>
818 or not std::is_nothrow_move_constructible_v<related_type>;
819
820 if constexpr (do_simple_emplace)
821 emplace<new_index>(PLG_FWD(t));
822 else {
824 emplace<new_index>(PLG_MOV(tmp));
825 }
826 }
827
828 return *this;
829 }
830
831 // ================================== modifiers (20.7.3.5)
832
833 template<class T, class... Args>
834#ifdef __cpp_concepts
835 requires (std::is_constructible_v<T, Args&&...> && detail::appears_exactly_once<T, Ts...>)
836#endif // __cpp_concepts
837 constexpr T& emplace(Args&&... args) {
838 return emplace<index_of<T>>(static_cast<Args&&>(args)...);
839 }
840
841 template<std::size_t Idx, class... Args>
842#ifdef __cpp_concepts
844#endif // __cpp_concepts
845 constexpr auto& emplace(Args&&... args) {
846 return emplace_impl<Idx>(PLG_FWD(args)...);
847 }
848
849 // emplace with initializer-lists
850 template<std::size_t Idx, class U, class... Args>
851#ifdef __cpp_concepts
852 requires (Idx < size
853 && std::is_constructible_v<alternative<Idx>, std::initializer_list<U>&, Args&&...>)
854#endif // __cpp_concepts
855 constexpr auto& emplace(std::initializer_list<U> list, Args&&... args) {
856 return emplace_impl<Idx>(list, PLG_FWD(args)...);
857 }
858
859 template<class T, class U, class... Args>
860#ifdef __cpp_concepts
861 requires (std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>
862 && detail::appears_exactly_once<T, Ts...>)
863#endif // __cpp_concepts
864 constexpr T& emplace(std::initializer_list<U> list, Args&&... args) {
865 return emplace_impl<index_of<T>>(list, PLG_FWD(args)...);
866 }
867
868 // ==================================== value status (20.7.3.6)
869
870 [[nodiscard]] constexpr bool valueless_by_exception() const noexcept {
871 if constexpr (can_be_valueless)
872 return _current == npos;
873 else return false;
874 }
875
876 [[nodiscard]] constexpr index_type index() const noexcept {
877 return _current;
878 }
879
880 // =================================== swap (20.7.3.7)
881
882 constexpr void swap(variant& o)
883 noexcept((std::is_nothrow_move_constructible_v<Ts> && ...)
884 && (detail::swap_trait::template nothrow<Ts> && ...))
886 requires (has_move_ctor && (detail::swap_trait::template able<Ts> && ...))
887#endif // __cpp_concepts
888 {
889 if constexpr (can_be_valueless) {
890 // if one is valueless, move the element form the non-empty variant,
891 // reset it, and set it to valueless
892 constexpr auto impl_one_valueless = [](auto&& full, auto& empty) {
893 detail::visit_with_index(PLG_FWD(full), detail::emplace_no_dtor_from_elem<variant&>{empty});
894 full.reset_no_check();
895 full._current = npos;
896 };
897
898 switch(static_cast<int>(index() == npos) + static_cast<int>(o.index() == npos) * 2) {
899 case 0 :
900 break;
901 case 1 :
902 // "this" is valueless
903 impl_one_valueless(PLG_MOV(o), *this);
904 return;
905 case 2 :
906 // "other" is valueless
907 impl_one_valueless(PLG_MOV(*this), o);
908 return;
909 case 3 :
910 // both are valueless, do nothing
911 return;
912 }
913 }
914
915 assert(not(valueless_by_exception() && o.valueless_by_exception()));
916
917 detail::visit_with_index(o, [&o, this](auto&& elem, auto index_cst) {
918 if (index() == index_cst) {
919 using std::swap;
921 return;
922 }
923
924 using idx_t = decltype(index_cst);
925 detail::visit_with_index(*this, [this, &o, &elem](auto&& this_elem, auto this_index) {
926 auto tmp { PLG_MOV(this_elem) };
927
928 // destruct the element
929 detail::destruct<alternative<this_index>>(this_elem);
930
931 // ok, we just destroyed the element in this, don't call the dtor again
933
934 // we could refactor this
935 detail::destruct<alternative<idx_t::value>>(elem);
937 });
938 });
939 }
940
941 // +================================== methods for internal use
942 // these methods performs no errors checking at all
943
944 template<detail::union_index_t Idx>
945 [[nodiscard]] constexpr auto& unsafe_get() & noexcept {
946 static_assert(Idx < size);
947 assert(_current == Idx);
948 return _storage.template get<Idx>();
949 }
950
951 template<detail::union_index_t Idx>
952 [[nodiscard]] constexpr auto&& unsafe_get() && noexcept {
953 static_assert(Idx < size);
954 assert(_current == Idx);
955 return PLG_MOV(_storage.template get<Idx>());
956 }
957
958 template<detail::union_index_t Idx>
959 [[nodiscard]] constexpr const auto& unsafe_get() const & noexcept {
960 static_assert(Idx < size);
961 assert(_current == Idx);
962 return _storage.template get<Idx>();
963 }
964
965 template<detail::union_index_t Idx>
966 [[nodiscard]] constexpr const auto&& unsafe_get() const && noexcept {
967 static_assert(Idx < size);
968 assert(_current == Idx);
969 return PLG_MOV(_storage.template get<Idx>());
970 }
971
972 private:
973 // assign from another variant
974 template<class Other, class Fn>
975 constexpr void assign_from(Other&& o, Fn&& fn) {
976 if constexpr (can_be_valueless) {
977 if (o.index() == npos) {
978 if (_current != npos) {
979 reset_no_check();
980 _current = npos;
981 }
982 return;
983 }
984 }
985 assert(not o.valueless_by_exception());
986 detail::visit_with_index(PLG_FWD(o), PLG_FWD(fn));
987 }
988
989 template<unsigned Idx, class... Args>
990 constexpr auto& emplace_impl(Args&&... args) {
991 reset();
992 emplace_no_dtor<Idx>(PLG_FWD(args)...);
993 return unsafe_get<Idx>();
994 }
995
996 // can be used directly only when the variant is in a known empty state
997 template<unsigned Idx, class... Args>
998 constexpr void emplace_no_dtor(Args&&... args) {
999 using T = alternative<Idx>;
1000
1001 if constexpr (not std::is_nothrow_constructible_v<T, Args&&...>)
1002 {
1003 if constexpr (std::is_nothrow_move_constructible_v<T>)
1004 {
1005 do_emplace_no_dtor<Idx>(T{PLG_FWD(args)...});
1006 }
1007 else if constexpr (std::is_nothrow_copy_constructible_v<T>)
1008 {
1009 T tmp {PLG_FWD(args)...};
1011 }
1012 else
1013 {
1014 static_assert(can_be_valueless,
1015 "Internal error : the possibly valueless branch of emplace was taken despite |can_be_valueless| being false");
1016 _current = npos;
1017 do_emplace_no_dtor<Idx>(PLG_FWD(args)...);
1018 }
1019 }
1020 else
1021 do_emplace_no_dtor<Idx>(PLG_FWD(args)...);
1022 }
1023
1024 template<unsigned Idx, class... Args>
1025 constexpr void do_emplace_no_dtor(Args&&... args) {
1026 _current = static_cast<index_type>(Idx);
1027
1028 auto* ptr = detail::addressof(unsafe_get<Idx>());
1029
1030#ifdef PLUGIFY_VARIANT_NO_CONSTEXPR_EMPLACE
1031 using T = alternative<Idx>;
1032 new ((void*)(ptr)) t(PLG_FWD(args)...);
1033#else
1034 PLUGIFY_CONSTRUCT_AT(ptr, PLG_FWD(args)...);
1035#endif // PLUGIFY_VARIANT_NO_CONSTEXPR_EMPLACE
1036 }
1037
1038 // destroy the current elem IFF not valueless
1039 constexpr void reset() {
1040 if constexpr (can_be_valueless)
1041 if (valueless_by_exception()) return;
1042 reset_no_check();
1043 }
1044
1045 // destroy the current element without checking for valueless
1046 constexpr void reset_no_check() {
1047 assert(index() < size);
1048 if constexpr (not trivial_dtor) {
1049 detail::visit_with_index(*this, [](auto& elem, auto index_cst) {
1050 detail::destruct<alternative<index_cst>>(elem);
1051 });
1052 }
1053 }
1054
1055 // construct this from another variant, for constructors only
1056 template<class Other>
1057 constexpr void construct_from(Other&& o) {
1058 if constexpr (can_be_valueless)
1059 if (o.valueless_by_exception()) {
1060 _current = npos;
1061 return;
1062 }
1063
1064 detail::visit_with_index(PLG_FWD(o), detail::emplace_no_dtor_from_elem<variant&>{*this});
1065 }
1066
1067 template<class T>
1069
1070 storage _storage;
1071 index_type _current;
1072 };
1073
1074 // ================================= value access (20.7.5)
1075
1076 template<class T, class... Ts>
1077 constexpr bool holds_alternative(const variant<Ts...>& v) noexcept {
1078 static_assert((std::is_same_v<T, Ts> || ...), "Requested type is not contained in the variant");
1079 constexpr auto Index = variant<Ts...>::template index_of<T>;
1080 return v.index() == Index;
1081 }
1082
1083 // ========= get by index
1084
1085 template<std::size_t Idx, class... Ts>
1086 [[nodiscard]] constexpr auto& get(variant<Ts...>& v) {
1087 static_assert(Idx < sizeof...(Ts), "Index exceeds the variant size. ");
1088 PLUGIFY_ASSERT(v.index() == Idx, "plg::variant:get(): Bad variant access in get.", bad_variant_access);
1089 return (v.template unsafe_get<Idx>());
1090 }
1091
1092 template<std::size_t Idx, class... Ts>
1093 [[nodiscard]] constexpr const auto& get(const variant<Ts...>& v) {
1094 return plg::get<Idx>(const_cast<variant<Ts...>&>(v));
1095 }
1096
1097 template<std::size_t Idx, class... Ts>
1098 [[nodiscard]] constexpr auto&& get(variant<Ts...>&& v) {
1099 return PLG_MOV(plg::get<Idx>(v));
1100 }
1101
1102 template<std::size_t Idx, class... Ts>
1103 [[nodiscard]] constexpr const auto&& get(const variant<Ts...>&& v) {
1104 return PLG_MOV(plg::get<Idx>(v));
1105 }
1106
1107 // ========= get by type
1108
1109 template<class T, class... Ts>
1110 [[nodiscard]] constexpr T& get(variant<Ts...>& v) {
1111 return plg::get<variant<Ts...>::template index_of<T> >(v);
1112 }
1113
1114 template<class T, class... Ts>
1115 [[nodiscard]] constexpr const T& get(const variant<Ts...>& v) {
1116 return plg::get<variant<Ts...>::template index_of<T> >(v);
1117 }
1118
1119 template<class T, class... Ts>
1120 [[nodiscard]] constexpr T&& get(variant<Ts...>&& v) {
1121 return plg::get<variant<Ts...>::template index_of<T> >(PLG_FWD(v));
1122 }
1123
1124 template<class T, class... Ts>
1125 [[nodiscard]] constexpr const T&& get(const variant<Ts...>&& v) {
1126 return plg::get<variant<Ts...>::template index_of<T> >(PLG_FWD(v));
1127 }
1128
1129 // ===== get_if by index
1130
1131 template<std::size_t Idx, class... Ts>
1132 [[nodiscard]] constexpr const auto* get_if(const variant<Ts...>* v) noexcept {
1133 using rtype = typename variant<Ts...>::template alternative<Idx>*;
1134 if (v == nullptr || v->index() != Idx)
1135 return rtype{nullptr};
1136 else
1137 return detail::addressof(v->template unsafe_get<Idx>());
1138 }
1139
1140 template<std::size_t Idx, class... Ts>
1141 [[nodiscard]] constexpr auto* get_if(variant<Ts...>* v) noexcept {
1142 using rtype = typename variant<Ts...>::template alternative<Idx>;
1143 return const_cast<rtype*>(
1144 plg::get_if<Idx>(static_cast<const variant<Ts...>*>(v))
1145 );
1146 }
1147
1148 // ====== get_if by type
1149
1150 template<class T, class... Ts>
1151 [[nodiscard]] constexpr T* get_if(variant<Ts...>* v) noexcept {
1152 static_assert((std::is_same_v<T, Ts> || ...), "Requested type is not contained in the variant");
1153 return plg::get_if<variant<Ts...>::template index_of<T> >(v);
1154 }
1155
1156 template<class T, class... Ts>
1157 [[nodiscard]] constexpr const T* get_if(const variant<Ts...>* v) noexcept {
1158 static_assert((std::is_same_v<T, Ts> || ...), "Requested type is not contained in the variant");
1159 return plg::get_if<variant<Ts...>::template index_of<T> >(v);
1160 }
1161
1162 // =============================== visitation (20.7.7)
1163
1164 template<class Fn, class... Vs>
1165 constexpr decltype(auto) visit(Fn&& fn, Vs&&... vs) {
1166 if constexpr ((std::decay_t<Vs>::can_be_valueless || ...))
1167 PLUGIFY_ASSERT(!(vs.valueless_by_exception() || ...), "plg::variant:visit(): Bad variant access in visit.", bad_variant_access);
1168
1169 if constexpr (sizeof...(Vs) == 1)
1170 return detail::visit(PLG_FWD(fn), PLG_FWD(vs)...);
1171 else
1172 return detail::multi_visit(PLG_FWD(fn), PLG_FWD(vs)...);
1173 }
1174
1175 template<class Fn>
1176 constexpr decltype(auto) visit(Fn&& fn) {
1177 return PLG_FWD(fn)();
1178 }
1179
1180 template<class R, class Fn, class... Vs>
1181#ifdef __cpp_concepts
1182 requires (is_variant_v<Vs> && ...)
1183#endif // __cpp_concepts
1184 constexpr R visit(Fn&& fn, Vs&&... vars) {
1185 return static_cast<R>(plg::visit(PLG_FWD(fn), PLG_FWD(vars)...));
1186 }
1187
1188 // ============================== relational operators (20.7.6)
1189
1190 template<class... Ts>
1191#ifdef __cpp_concepts
1192 requires (detail::has_eq_comp<Ts> && ...)
1193#endif // __cpp_concepts
1194 [[nodiscard]] constexpr bool operator==(const variant<Ts...>& v1, const variant<Ts...>& v2) {
1195 if (v1.index() != v2.index())
1196 return false;
1197 if constexpr (variant<Ts...>::can_be_valueless)
1198 if (v1.valueless_by_exception()) return true;
1199 return detail::visit_with_index(v2, [&v1](auto& elem, auto index) -> bool {
1200 return (v1.template unsafe_get<index>() == elem);
1201 });
1202 }
1203
1204 template<class... Ts>
1205 [[nodiscard]] constexpr bool operator!=(const variant<Ts...>& v1, const variant<Ts...>& v2)
1206#ifdef __cpp_concepts
1207 requires requires { v1 == v2; }
1208#endif // __cpp_concepts
1209 {
1210 return not(v1 == v2);
1211 }
1212
1213 template<class... Ts>
1214#ifdef __cpp_concepts
1215 requires (detail::has_lesser_comp<const Ts&> && ...)
1216#endif // __cpp_concepts
1217 [[nodiscard]] constexpr bool operator<(const variant<Ts...>& v1, const variant<Ts...>& v2) {
1218 if constexpr (variant<Ts...>::can_be_valueless) {
1219 if (v2.valueless_by_exception()) return false;
1220 if (v1.valueless_by_exception()) return true;
1221 }
1222 if (v1.index() == v2.index()) {
1223 return detail::visit_with_index(v1, [&v2](auto& elem, auto index) -> bool {
1224 return (elem < v2.template unsafe_get<index>());
1225 });
1226 }
1227 else
1228 return (v1.index() < v2.index());
1229 }
1230
1231 template<class... Ts>
1232 [[nodiscard]] constexpr bool operator>(const variant<Ts...>& v1, const variant<Ts...>& v2)
1233#ifdef __cpp_concepts
1234 requires requires { v2 < v1; }
1235#endif // __cpp_concepts
1236 {
1237 return v2 < v1;
1238 }
1239
1240 template<class... Ts>
1241#ifdef __cpp_concepts
1242 requires (detail::has_less_or_eq_comp<const Ts&> && ...)
1243#endif // __cpp_concepts
1244 [[nodiscard]] constexpr bool operator<=(const variant<Ts...>& v1, const variant<Ts...>& v2) {
1245 if constexpr (variant<Ts...>::can_be_valueless) {
1246 if (v1.valueless_by_exception()) return true;
1247 if (v2.valueless_by_exception()) return false;
1248 }
1249 if (v1.index() == v2.index()) {
1250 return detail::visit_with_index(v1, [&v2](auto& elem, auto index) -> bool {
1251 return (elem <= v2.template unsafe_get<index>());
1252 });
1253 }
1254 else
1255 return (v1.index() < v2.index());
1256 }
1257
1258 template<class... Ts>
1259 [[nodiscard]] constexpr bool operator>=(const variant<Ts...>& v1, const variant<Ts...>& v2)
1260#ifdef __cpp_concepts
1261 requires requires { v2 <= v1; }
1262#endif // __cpp_concepts
1263 {
1264 return v2 <= v1;
1265 }
1266
1267 // ===================================== monostate (20.7.8, 20.7.9)
1268
1269 struct monostate{};
1270 [[nodiscard]] constexpr bool operator==(monostate, monostate) noexcept { return true; }
1271 [[nodiscard]] constexpr bool operator> (monostate, monostate) noexcept { return false; }
1272 [[nodiscard]] constexpr bool operator< (monostate, monostate) noexcept { return false; }
1273 [[nodiscard]] constexpr bool operator<=(monostate, monostate) noexcept { return true; }
1274 [[nodiscard]] constexpr bool operator>=(monostate, monostate) noexcept { return true; }
1275
1276 // ===================================== specialized algorithms (20.7.10)
1277
1278 template<class... Ts>
1279 constexpr void swap(variant<Ts...>& a, variant<Ts...>& b)
1280 noexcept(noexcept(a.swap(b)))
1281#ifdef __cpp_concepts
1282 requires requires { a.swap(b); }
1283#endif // __cpp_concepts
1284 {
1285 a.swap(b);
1286 }
1287
1288 // ===================================== helper classes (20.7.4)
1289
1290 template<class T>
1291#ifdef __cpp_concepts
1292 requires is_variant_v<T>
1293#endif // __cpp_concepts
1294 inline constexpr std::size_t variant_size_v = std::decay_t<T>::size;
1295
1296 // not sure why anyone would need this, i'm adding it anyway
1297 template<class T>
1298#ifdef __cpp_concepts
1299 requires is_variant_v<T>
1300#endif // __cpp_concepts
1301 struct variant_size : std::integral_constant<std::size_t, variant_size_v<T>> {};
1302
1303 namespace detail {
1304 // ugh, we have to take care of volatile here
1305 template<bool IsVolatile>
1306 struct var_alt_impl {
1307 template<std::size_t Idx, class T>
1308 using type = std::remove_reference_t<decltype(std::declval<T>().template unsafe_get<Idx>())>;
1309 };
1310
1311 template<>
1312 struct var_alt_impl<true> {
1313 template<std::size_t Idx, class T>
1314 using type = volatile typename var_alt_impl<false>::template type<Idx, std::remove_volatile_t<T>>;
1315 };
1316 }
1317
1318 template<std::size_t Idx, class T>
1319#ifdef __cpp_concepts
1320 requires (Idx < variant_size_v<T>)
1321#endif // __cpp_concepts
1322 using variant_alternative_t = typename detail::var_alt_impl<std::is_volatile_v<T>>::template type<Idx, T>;
1323
1324 template<std::size_t Idx, class T>
1325#ifdef __cpp_concepts
1326 requires is_variant_v<T>
1327#endif // __cpp_concepts
1331
1332 // ===================================== extensions (unsafe_get)
1333
1334 template<std::size_t Idx, class Var>
1335#ifdef __cpp_concepts
1336 requires is_variant_v<Var>
1337#endif // __cpp_concepts
1338 [[nodiscard]] constexpr auto&& unsafe_get(Var&& var) noexcept {
1339 static_assert(Idx < std::decay_t<Var>::size, "Index exceeds the variant size.");
1340 return PLG_FWD(var).template unsafe_get<Idx>();
1341 }
1342
1343 template<class T, class Var>
1344#ifdef __cpp_concepts
1345 requires is_variant_v<Var>
1346#endif // __cpp_concepts
1347 [[nodiscard]] constexpr auto&& unsafe_get(Var&& var) noexcept {
1348 return plg::unsafe_get<std::decay_t<Var>::template index_of<T>>(PLG_FWD(var));
1349 }
1350
1351} // namespace plg
1352
1353// ====================================== hash support(20.7.12)
1354#ifndef PLUGIFY_VARIANT_NO_STD_HASH
1355namespace std {
1356 template<class... Ts>
1357#ifdef __cpp_concepts
1358 requires (plg::detail::has_std_hash<Ts> && ...)
1359#endif // __cpp_concepts
1360 struct hash<plg::variant<Ts...>> {
1361 std::size_t operator()(const plg::variant<Ts...>& v) const {
1362 if constexpr (plg::variant<Ts...>::can_be_valueless)
1363 if (v.valueless_by_exception())
1364 return static_cast<std::size_t>(-1);
1365
1366 return plg::detail::visit_with_index(v, [](auto& elem, auto index_) {
1367 using type = std::remove_cvref_t<decltype(elem)>;
1368 return std::hash<type>{}(elem) + index_;
1369 });
1370 }
1371 };
1372
1373 template<>
1374 struct hash<plg::monostate> {
1375 constexpr std::size_t operator()(plg::monostate) const noexcept { return static_cast<std::size_t>(-1); }
1376 };
1377} // namespace std
1378#endif // PLUGIFY_VARIANT_NO_STD_HASH
1379
1380#undef PLG_FWD
1381#undef PLG_MOV