GCC Code Coverage Report


Directory: include/sumty/
File: include/sumty/detail/variant_impl.hpp
Date: 2024-04-28 13:27:51
Exec Total Coverage
Lines: 202 241 83.8%
Functions: 436 620 70.3%
Branches: 23 30 76.7%
Decisions: 21 26 80.8%

Line Branch Decision Exec Source
1 /* Copyright 2023 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_DETAIL_VARIANT_IMPL_HPP
17 #define SUMTY_DETAIL_VARIANT_IMPL_HPP
18
19 #include "sumty/detail/auto_union.hpp"
20 #include "sumty/detail/traits.hpp"
21 #include "sumty/detail/utils.hpp"
22 #include "sumty/utils.hpp"
23
24 #include <cstddef>
25 #include <memory>
26 #include <new>
27 #include <type_traits>
28 #include <utility>
29
30 // IWYU pragma: no_include <string>
31 // IWYU pragma: no_include <variant>
32 // IWYU pragma: no_include <unordered_map>
33
34 #ifdef _MSC_VER
35 #pragma warning(push)
36 #pragma warning(disable : 4702)
37 #endif
38
39 namespace sumty::detail {
40
41 struct uninit_t {};
42
43 static inline constexpr uninit_t uninit{};
44
45 template <typename Enable, typename... T>
46 class variant_impl {
47 private:
48 using discrim_t = discriminant_t<sizeof...(T)>;
49
50 SUMTY_NO_UNIQ_ADDR auto_union<T...> data_;
51 discrim_t discrim_{};
52
53 template <size_t I>
54 30 constexpr void copy_construct(const auto_union<T...>& data) {
55 if constexpr (I < sizeof...(T)) {
56
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 5 times.
2/2
✓ Decision 'true' taken 10 times.
✓ Decision 'false' taken 5 times.
30 if (discrim_ == static_cast<discrim_t>(I)) {
57 if constexpr (std::is_void_v<select_t<I, T...>>) {
58 6 data_.template construct<I>();
59 } else {
60 14 data_.template construct<I>(data.template get<I>());
61 }
62 } else {
63 10 copy_construct<I + 1>(data);
64 }
65 }
66 30 }
67
68 template <size_t I>
69 36 constexpr void move_construct(auto_union<T...>& data) noexcept(
70 (true && ... && traits<T>::is_nothrow_move_constructible)) {
71 if constexpr (I < sizeof...(T)) {
72
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 8 times.
2/2
✓ Decision 'true' taken 10 times.
✓ Decision 'false' taken 8 times.
36 if (discrim_ == static_cast<discrim_t>(I)) {
73 if constexpr (std::is_void_v<select_t<I, T...>>) {
74 data_.template construct<I>();
75 } else if constexpr (std::is_lvalue_reference_v<select_t<I, T...>>) {
76 8 data_.template construct<I>(data.template get<I>());
77 } else {
78 12 data_.template construct<I>(std::move(data.template get<I>()));
79 }
80 } else {
81 16 move_construct<I + 1>(data);
82 }
83 }
84 36 }
85
86 template <size_t I>
87 862 constexpr void destroy() noexcept((true && ... && traits<T>::is_nothrow_destructible)) {
88 if constexpr (I < sizeof...(T)) {
89
2/2
✓ Branch 0 taken 262 times.
✓ Branch 1 taken 169 times.
2/2
✓ Decision 'true' taken 262 times.
✓ Decision 'false' taken 169 times.
862 if (discrim_ == static_cast<discrim_t>(I)) {
90 524 data_.template destroy<I>();
91 } else {
92 338 destroy<I + 1>();
93 }
94 }
95 862 }
96
97 template <size_t I>
98 constexpr void copy_assign(const auto_union<T...>& data) {
99 if constexpr (I < sizeof...(T)) {
100
0/2
✗ Decision 'true' not taken.
✗ Decision 'false' not taken.
if (discrim_ == static_cast<discrim_t>(I)) {
101 if constexpr (std::is_void_v<select_t<I, T...>>) {
102 data_.template construct<I>();
103 } else if constexpr (std::is_lvalue_reference_v<select_t<I, T...>>) {
104 data_.template construct<I>(data.template get<I>());
105 } else {
106 data_.template get<I>() = data.template get<I>();
107 }
108 } else {
109 copy_assign<I + 1>(data);
110 }
111 }
112 }
113
114 template <size_t I>
115 34 constexpr void move_assign(auto_union<T...>& data) noexcept(
116 (true && ... && traits<T>::is_nothrow_move_assignable)) {
117 if constexpr (I < sizeof...(T)) {
118
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 9 times.
2/2
✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 9 times.
34 if (discrim_ == static_cast<discrim_t>(I)) {
119 if constexpr (std::is_void_v<select_t<I, T...>>) {
120 2 data_.template construct<I>();
121 } else if constexpr (std::is_lvalue_reference_v<select_t<I, T...>>) {
122 6 data_.template construct<I>(data.template get<I>());
123 } else {
124 8 data_.template get<I>() = std::move(data.template get<I>());
125 }
126 } else {
127 18 move_assign<I + 1>(data);
128 }
129 }
130 34 }
131
132 template <size_t I>
133 14 constexpr void same_swap(auto_union<T...>& data) noexcept(
134 (true && ... && traits<T>::is_nothrow_swappable)) {
135 if constexpr (I < sizeof...(T)) {
136
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
2/2
✓ Decision 'true' taken 4 times.
✓ Decision 'false' taken 3 times.
14 if (discrim_ == static_cast<discrim_t>(I)) {
137 if constexpr (std::is_lvalue_reference_v<select_t<I, T...>>) {
138 auto* tmp = &data.template get<I>();
139 data.template construct<I>(data_.template get<I>());
140 data_.template construct<I>(*tmp);
141 } else if constexpr (!std::is_void_v<select_t<I, T...>>) {
142 using std::swap;
143 4 swap(data_.template get<I>(), data.template get<I>());
144 }
145 } else {
146 6 same_swap<I + 1>(data);
147 }
148 }
149 14 }
150
151 template <size_t I, size_t J>
152 6 constexpr void diff_swap_impl(variant_impl& other) noexcept((
153 true && ... &&
154 (traits<T>::is_nothrow_move_constructible && traits<T>::is_nothrow_destructible))) {
155 6 std::swap(discrim_, other.discrim_);
156 if constexpr (std::is_void_v<select_t<I, T...>>) {
157 if constexpr (std::is_void_v<select_t<J, T...>>) {
158 return;
159 } else if constexpr (std::is_lvalue_reference_v<select_t<J, T...>>) {
160 data_.template construct<J>(other.data_.template get<J>());
161 other.data_.template destroy<J>();
162 other.data_.template construct<I>();
163 } else {
164 2 data_.template construct<J>(std::move(other.data_.template get<J>()));
165 2 other.data_.template destroy<J>();
166 2 other.data_.template construct<I>();
167 }
168 } else if constexpr (std::is_lvalue_reference_v<select_t<I, T...>>) {
169 if constexpr (std::is_void_v<select_t<J, T...>>) {
170 2 other.data_.template construct<I>(data_.template get<I>());
171 2 data_.template destroy<I>();
172 2 data_.template construct<J>();
173 } else if constexpr (std::is_lvalue_reference_v<select_t<J, T...>>) {
174 auto& tmp = other.data_.template get<J>();
175 other.data_.template construct<I>(data_.template get<I>());
176 data_.template construct<J>(tmp);
177 } else {
178 auto& tmp = data_.template get<I>();
179 data_.template construct<J>(std::move(other.data_.template get<J>()));
180 other.data_.template destroy<J>();
181 other.data_.template construct<I>(tmp);
182 }
183 } else {
184 if constexpr (std::is_void_v<select_t<J, T...>>) {
185 other.data_.template construct<I>(std::move(data_.template get<I>()));
186 data_.template destroy<I>();
187 data_.template construct<J>();
188 } else if constexpr (std::is_lvalue_reference_v<select_t<J, T...>>) {
189 2 auto& tmp = other.data_.template get<J>();
190 2 other.data_.template construct<I>(std::move(data_.template get<I>()));
191 2 data_.template destroy<I>();
192 2 data_.template construct<J>(tmp);
193 } else {
194 auto&& tmp = std::move(other.data_.template get<J>());
195 other.data_.template destroy<J>();
196 other.data_.template construct<I>(std::move(data_.template get<I>()));
197 data_.template destroy<I>();
198 data_.template construct<J>(std::move(tmp));
199 }
200 }
201 6 }
202
203 template <size_t I, size_t J>
204 10 constexpr void diff_swap_nested(variant_impl& other) noexcept((
205 true && ... &&
206 (traits<T>::is_nothrow_move_constructible && traits<T>::is_nothrow_destructible))) {
207 if constexpr (J < sizeof...(T)) {
208
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
2/2
✓ Decision 'true' taken 3 times.
✓ Decision 'false' taken 2 times.
10 if (other.discrim_ == static_cast<discrim_t>(J)) {
209 6 diff_swap_impl<I, J>(other);
210 } else {
211 4 diff_swap_nested<I, J + 1>(other);
212 }
213 }
214 10 }
215
216 template <size_t I>
217 12 constexpr void diff_swap(variant_impl& other) noexcept((
218 true && ... &&
219 (traits<T>::is_nothrow_move_constructible && traits<T>::is_nothrow_destructible))) {
220 if constexpr (I < sizeof...(T)) {
221
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
2/2
✓ Decision 'true' taken 3 times.
✓ Decision 'false' taken 3 times.
12 if (discrim_ == static_cast<discrim_t>(I)) {
222 6 diff_swap_nested<I, 0>(other);
223 } else {
224 6 diff_swap<I + 1>(other);
225 }
226 }
227 12 }
228
229 public:
230 // NOLINTNEXTLINE(hicpp-explicit-conversions)
231 54 constexpr variant_impl([[maybe_unused]] uninit_t tag) noexcept {}
232
233 146 constexpr variant_impl() noexcept(
234 traits<first_t<T...>>::is_nothrow_default_constructible)
235 146 : variant_impl(std::in_place_index<0>) {}
236
237 18 constexpr variant_impl(const variant_impl& other) : discrim_(other.discrim_) {
238
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
18 copy_construct<0>(other.data_);
239 18 }
240
241 1 constexpr variant_impl(variant_impl&& other) noexcept(
242 (true && ... && traits<T>::is_nothrow_move_constructible))
243 1 : discrim_(other.discrim_) {
244 1 move_construct<0>(other.data_);
245 1 }
246
247 template <size_t I, typename... Args>
248 // NOLINTNEXTLINE(hicpp-explicit-conversions)
249 312 constexpr explicit(sizeof...(Args) == 0) variant_impl(
250 [[maybe_unused]] std::in_place_index_t<I> inplace,
251 Args&&... args) noexcept(traits<select_t<I, T...>>::
252 template is_nothrow_constructible<Args...>)
253 312 : discrim_(static_cast<discrim_t>(I)) {
254
1/2
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
312 data_.template construct<I>(std::forward<Args>(args)...);
255 312 }
256
257 386 constexpr ~variant_impl() noexcept((true && ... &&
258 traits<T>::is_nothrow_destructible)) {
259 386 destroy<0>();
260 386 }
261
262 1 constexpr variant_impl& operator=(const variant_impl& rhs) {
263
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1/2
✓ Decision 'true' taken 1 times.
✗ Decision 'false' not taken.
1 if (this != &rhs) {
264
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 1 times.
1 if (discrim_ == rhs.discrim_) {
265 copy_assign<0>(rhs.data_);
266 } else {
267 1 destroy<0>();
268 1 discrim_ = rhs.discrim_;
269 1 copy_construct<0>(rhs.data_);
270 }
271 }
272 1 return *this;
273 }
274
275 34 constexpr variant_impl& operator=(variant_impl&& rhs) noexcept((
276 true && ... &&
277 (traits<T>::is_nothrow_move_assignable &&
278 traits<T>::is_nothrow_move_constructible && traits<T>::is_nothrow_destructible))) {
279
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 9 times.
2/2
✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 9 times.
34 if (discrim_ == rhs.discrim_) {
280 16 move_assign<0>(rhs.data_);
281 } else {
282 18 destroy<0>();
283 18 discrim_ = rhs.discrim_;
284 18 move_construct<0>(rhs.data_);
285 }
286 34 return *this;
287 }
288
289 1036 [[nodiscard]] constexpr size_t index() const noexcept {
290 1036 return static_cast<size_t>(discrim_);
291 }
292
293 template <size_t I>
294 454 [[nodiscard]] constexpr typename traits<select_t<I, T...>>::reference get() & noexcept {
295 454 return data_.template get<I>();
296 }
297
298 template <size_t I>
299 214 [[nodiscard]] constexpr typename traits<select_t<I, T...>>::const_reference get()
300 const& noexcept {
301 214 return data_.template get<I>();
302 }
303
304 template <size_t I>
305 13 [[nodiscard]] constexpr typename traits<select_t<I, T...>>::rvalue_reference get() && {
306 if constexpr (std::is_void_v<select_t<I, T...>>) {
307 return;
308 } else if constexpr (std::is_lvalue_reference_v<select_t<I, T...>>) {
309 6 return data_.template get<I>();
310 } else {
311 7 return std::move(data_.template get<I>());
312 }
313 }
314
315 template <size_t I>
316 14 [[nodiscard]] constexpr typename traits<select_t<I, T...>>::const_rvalue_reference get()
317 const&& {
318 if constexpr (std::is_void_v<select_t<I, T...>>) {
319 return;
320 } else if constexpr (std::is_lvalue_reference_v<select_t<I, T...>>) {
321 6 return data_.template get<I>();
322 } else {
323 8 return std::move(data_.template get<I>());
324 }
325 }
326
327 template <size_t I>
328 [[nodiscard]] constexpr typename traits<select_t<I, T...>>::pointer ptr() noexcept {
329 if constexpr (!std::is_void_v<select_t<I, T...>>) {
330 return &data_.template get<I>();
331 } else {
332 return;
333 }
334 }
335
336 template <size_t I>
337 [[nodiscard]] constexpr typename traits<select_t<I, T...>>::const_pointer ptr()
338 const noexcept {
339 if constexpr (!std::is_void_v<select_t<I, T...>>) {
340 return &data_.template get<I>();
341 } else {
342 return;
343 }
344 }
345
346 template <size_t I, typename... Args>
347 118 constexpr void emplace(Args&&... args) {
348 118 destroy<0>();
349 118 data_.template construct<I>(std::forward<Args>(args)...);
350 118 discrim_ = static_cast<discrim_t>(I);
351 118 }
352
353 template <size_t I, typename... Args>
354 54 constexpr void uninit_emplace(Args&&... args) {
355 54 data_.template construct<I>(std::forward<Args>(args)...);
356 54 discrim_ = static_cast<discrim_t>(I);
357 54 }
358
359 7 constexpr void swap(variant_impl& other) noexcept(
360 (true && ... &&
361 (traits<T>::is_nothrow_swappable && traits<T>::is_nothrow_move_constructible &&
362 traits<T>::is_nothrow_destructible))) {
363
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
2/2
✓ Decision 'true' taken 4 times.
✓ Decision 'false' taken 3 times.
7 if (discrim_ == other.discrim_) {
364 4 same_swap<0>(other.data_);
365 } else {
366 3 diff_swap<0>(other);
367 }
368 7 }
369 };
370
371 template <>
372 class variant_impl<void> {};
373
374 template <typename... T>
375 struct empty_tuple;
376
377 template <>
378 struct empty_tuple<> {};
379
380 template <typename... TN>
381 struct empty_tuple<void, TN...> : empty_tuple<TN...> {
382 template <size_t I>
383 constexpr typename traits<select_t<I, void, TN...>>::reference get() noexcept {
384 if constexpr (I != 0) {
385 static_cast<empty_tuple<TN...>*>(this)->template get<I - 1>();
386 }
387 }
388
389 template <size_t I>
390 constexpr typename traits<select_t<I, void, TN...>>::const_reference get()
391 const noexcept {
392 if constexpr (I != 0) {
393 static_cast<const empty_tuple<TN...>*>(this)->template get<I - 1>();
394 }
395 }
396 };
397
398 template <typename T0, typename... TN>
399 requires(std::is_base_of_v<never, std::remove_cvref_t<T0>>)
400 struct empty_tuple<T0, TN...> : empty_tuple<TN...> {
401 template <size_t I>
402 // NOLINTNEXTLINE(bugprone-exception-escape)
403 constexpr typename traits<select_t<I, never, TN...>>::reference get() noexcept {
404 if constexpr (I == 0) {
405 if (std::is_constant_evaluated()) {
406 throw 0; // NOLINT(hicpp-exception-baseclass)
407 } else {
408 void* ptr = nullptr;
409 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
410 return *reinterpret_cast<never*>(ptr);
411 }
412 } else {
413 static_cast<empty_tuple<TN...>*>(this)->template get<I - 1>();
414 }
415 }
416
417 template <size_t I>
418 // NOLINTNEXTLINE(bugprone-exception-escape)
419 constexpr typename traits<select_t<I, never, TN...>>::const_reference get()
420 const noexcept {
421 if constexpr (I == 0) {
422 if (std::is_constant_evaluated()) {
423 throw 0; // NOLINT(hicpp-exception-baseclass)
424 } else {
425 const void* ptr = nullptr;
426 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
427 return *reinterpret_cast<const never*>(ptr);
428 }
429 } else {
430 static_cast<const empty_tuple<TN...>*>(this)->template get<I - 1>();
431 }
432 }
433 };
434
435 template <typename T0, typename... TN>
436 requires(!std::is_void_v<T0> && !std::is_base_of_v<never, std::remove_cvref_t<T0>>)
437 struct empty_tuple<T0, TN...> : empty_tuple<TN...> {
438 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
439 static inline T0 head_{};
440
441 template <size_t I>
442 constexpr typename traits<select_t<I, T0, TN...>>::reference get() noexcept {
443 if constexpr (I == 0) {
444 return head_;
445 } else {
446 static_cast<empty_tuple<TN...>*>(this)->template get<I - 1>();
447 }
448 }
449
450 template <size_t I>
451 constexpr typename traits<select_t<I, T0, TN...>>::const_reference get()
452 const noexcept {
453 if constexpr (I == 0) {
454 return head_;
455 } else {
456 static_cast<const empty_tuple<TN...>*>(this)->template get<I - 1>();
457 }
458 }
459 };
460
461 template <typename T>
462 concept void_or_empty_or_never = std::is_void_v<T> || is_sumty_empty_v<T> ||
463 std::is_base_of_v<never, std::remove_cvref_t<T>>;
464
465 template <void_or_empty_or_never... T>
466 class variant_impl<
467 std::enable_if_t<(sizeof...(T) > 1) &&
468 !std::is_base_of_v<never, std::remove_cvref_t<last_t<T...>>> &&
469 !std::is_base_of_v<never, std::remove_cvref_t<select_t<0, T...>>>>,
470 T...> {
471 private:
472 using discrim_t = discriminant_t<sizeof...(T)>;
473
474 discrim_t discrim_{};
475 SUMTY_NO_UNIQ_ADDR empty_tuple<T...> data_{};
476
477 public:
478 // NOLINTNEXTLINE(hicpp-explicit-conversions)
479 constexpr variant_impl([[maybe_unused]] uninit_t tag) noexcept {}
480
481 2 constexpr variant_impl() noexcept = default;
482
483 constexpr variant_impl(const variant_impl&) noexcept = default;
484
485 constexpr variant_impl(variant_impl&&) noexcept = default;
486
487 template <size_t I, typename... Args>
488 constexpr explicit(sizeof...(Args) == 0)
489 // NOLINTNEXTLINE(hicpp-explicit-conversions)
490 10 variant_impl([[maybe_unused]] std::in_place_index_t<I> inplace, Args&&... args)
491 10 : discrim_(static_cast<discrim_t>(I)) {
492 #ifdef _MSC_VER
493 // I don't know why, but MSVC seems to skip the initializer for
494 // discrim_ above, so we manually assign it again here.
495 discrim_ = static_cast<discrim_t>(I);
496 #endif
497 if constexpr (sizeof...(Args) > 0) {
498 std::construct_at(&data_.template get<I>(), std::forward<Args>(args)...);
499 }
500 10 }
501
502 constexpr ~variant_impl() noexcept = default;
503
504 constexpr variant_impl& operator=(const variant_impl&) = default;
505
506 constexpr variant_impl& operator=(variant_impl&&) = default;
507
508 17 [[nodiscard]] constexpr size_t index() const noexcept {
509 17 return static_cast<size_t>(discrim_);
510 }
511
512 template <size_t I>
513 [[nodiscard]] constexpr typename traits<select_t<I, T...>>::reference get() noexcept {
514 if constexpr (!std::is_void_v<select_t<I, T...>>) {
515 return data_.template get<I>();
516 }
517 }
518
519 template <size_t I>
520 1 [[nodiscard]] constexpr typename traits<select_t<I, T...>>::const_reference get()
521 const noexcept {
522 if constexpr (!std::is_void_v<select_t<I, T...>>) {
523 return data_.template get<I>();
524 }
525 1 }
526
527 template <size_t I>
528 [[nodiscard]] constexpr typename traits<select_t<I, T...>>::pointer ptr() noexcept {
529 if constexpr (!std::is_void_v<select_t<I, T...>>) {
530 return &data_.template get<I>();
531 }
532 }
533
534 template <size_t I>
535 [[nodiscard]] constexpr typename traits<select_t<I, T...>>::const_pointer ptr()
536 const noexcept {
537 if constexpr (!std::is_void_v<select_t<I, T...>>) {
538 return &data_.template get<I>();
539 }
540 }
541
542 template <size_t I, typename... Args>
543 constexpr void emplace([[maybe_unused]] Args&&... args) {
544 discrim_ = static_cast<discrim_t>(I);
545 if constexpr (sizeof...(Args) > 0 && !std::is_void_v<select_t<I, T...>>) {
546 std::construct_at(ptr<I>(), std::forward<Args>(args)...);
547 }
548 }
549
550 template <size_t I, typename... Args>
551 constexpr void uninit_emplace([[maybe_unused]] Args&&... args) {
552 discrim_ = static_cast<discrim_t>(I);
553 if constexpr (sizeof...(Args) > 0 && !std::is_void_v<select_t<I, T...>>) {
554 std::construct_at(ptr<I>(), std::forward<Args>(args)...);
555 }
556 }
557
558 constexpr void swap(variant_impl& other) noexcept {
559 std::swap(discrim_, other.discrim_);
560 }
561 };
562
563 template <typename T>
564 class variant_impl<std::enable_if_t<!void_or_empty_or_never<T>>, T> {
565 private:
566 SUMTY_NO_UNIQ_ADDR auto_union<T> data_;
567
568 public:
569 // NOLINTNEXTLINE(hicpp-explicit-conversions)
570 constexpr variant_impl([[maybe_unused]] uninit_t tag) noexcept {}
571
572 3 constexpr variant_impl() noexcept(traits<T>::is_nothrow_default_constructible) {
573 3 data_.template construct<0>();
574 3 }
575
576 2 constexpr variant_impl(const variant_impl& other) {
577 2 data_.template construct<0>(other.data_.template get<0>());
578 2 }
579
580 2 constexpr variant_impl(variant_impl&& other) noexcept(
581 2 traits<T>::is_nothrow_move_constructible) {
582 2 data_.template construct<0>(other.data_.template get<0>());
583 2 }
584
585 template <typename... Args>
586 constexpr explicit(sizeof...(Args) == 0)
587 // NOLINTNEXTLINE(hicpp-explicit-conversions)
588 51 variant_impl([[maybe_unused]] std::in_place_index_t<0> inplace, Args&&... args) {
589 51 data_.template construct<0>(std::forward<Args>(args)...);
590 51 }
591
592 66 constexpr ~variant_impl() noexcept(traits<T>::is_nothrow_destructible) {
593 66 data_.template destroy<0>();
594 66 }
595
596 2 constexpr variant_impl& operator=(const variant_impl& rhs) {
597
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
1/2
✓ Decision 'true' taken 2 times.
✗ Decision 'false' not taken.
2 if (this != &rhs) {
598 if constexpr (std::is_lvalue_reference_v<T>) {
599 data_.template construct<0>(rhs.data_.template get<0>());
600 } else {
601 2 data_.template get<0>() = rhs.data_.template get<0>();
602 }
603 }
604 2 return *this;
605 }
606
607 2 constexpr variant_impl& operator=(variant_impl&& rhs) noexcept(
608 traits<T>::is_nothrow_move_assignable) {
609 if constexpr (std::is_lvalue_reference_v<T>) {
610 data_.template construct<0>(rhs.data_.template get<0>());
611 } else {
612 2 data_.template get<0>() = std::move(rhs.data_.template get<0>());
613 }
614 2 return *this;
615 }
616
617 76 [[nodiscard]] static constexpr size_t index() noexcept { return 0; }
618
619 template <size_t I>
620 8 [[nodiscard]] constexpr typename traits<T>::reference get() & noexcept {
621 8 return data_.template get<I>();
622 }
623
624 template <size_t I>
625 1 [[nodiscard]] constexpr typename traits<T>::const_reference get() const& noexcept {
626 1 return data_.template get<I>();
627 }
628
629 template <size_t I>
630 39 [[nodiscard]] constexpr typename traits<T>::rvalue_reference get() && {
631 if constexpr (std::is_void_v<T>) {
632 return;
633 } else if constexpr (std::is_lvalue_reference_v<T>) {
634 8 return data_.template get<I>();
635 } else {
636 31 return std::move(data_.template get<I>());
637 }
638 }
639
640 template <size_t I>
641 [[nodiscard]] constexpr typename traits<T>::const_rvalue_reference get() const&& {
642 if constexpr (std::is_void_v<T>) {
643 return;
644 } else if constexpr (std::is_lvalue_reference_v<T>) {
645 return data_.template get<I>();
646 } else {
647 return std::move(data_.template get<I>());
648 }
649 }
650
651 template <size_t I>
652 [[nodiscard]] constexpr typename traits<T>::pointer ptr() noexcept {
653 if constexpr (!std::is_void_v<T>) {
654 return &data_.template get<I>();
655 } else {
656 return;
657 }
658 }
659
660 template <size_t I>
661 [[nodiscard]] constexpr typename traits<T>::const_pointer ptr() const noexcept {
662 if constexpr (!std::is_void_v<T>) {
663 return &data_.template get<I>();
664 } else {
665 return;
666 }
667 }
668
669 template <size_t I, typename... Args>
670 constexpr void emplace(Args&&... args) {
671 data_.template destroy<0>();
672 data_.template construct<0>(std::forward<Args>(args)...);
673 }
674
675 template <size_t I, typename... Args>
676 constexpr void uninit_emplace(Args&&... args) {
677 data_.template construct<0>(std::forward<Args>(args)...);
678 }
679
680 constexpr void swap(variant_impl& other) noexcept(traits<T>::is_nothrow_swappable) {
681 if constexpr (std::is_void_v<T>) {
682 return;
683 } else if constexpr (std::is_lvalue_reference_v<T>) {
684 auto& tmp = other.data_.template get<0>();
685 other.data_.template construct<0>(data_.template get<0>());
686 data_.template construct<0>(tmp);
687 } else {
688 using std::swap;
689 swap(data_.template get<0>(), other.data_.template get<0>());
690 }
691 }
692 };
693
694 template <typename T>
695 class variant_impl<std::enable_if_t<is_sumty_empty_v<T>>, T> {
696 private:
697 SUMTY_NO_UNIQ_ADDR std::remove_const_t<T> data_;
698
699 public:
700 // NOLINTNEXTLINE(hicpp-explicit-conversions)
701 constexpr variant_impl([[maybe_unused]] uninit_t tag) noexcept {}
702
703 constexpr variant_impl() noexcept(traits<T>::is_nothrow_default_constructible)
704 : data_{} {}
705
706 constexpr variant_impl(const variant_impl& other) = default;
707
708 constexpr variant_impl(variant_impl&& other) noexcept(
709 traits<T>::is_nothrow_move_constructible) = default;
710
711 template <typename... Args>
712 constexpr explicit(sizeof...(Args) == 0)
713 // NOLINTNEXTLINE(hicpp-explicit-conversions)
714 variant_impl([[maybe_unused]] std::in_place_index_t<0> inplace, Args&&... args)
715 : data_(std::forward<Args>(args)...) {}
716
717 constexpr ~variant_impl() noexcept(traits<T>::is_nothrow_destructible) = default;
718
719 constexpr variant_impl& operator=(const variant_impl& rhs) = default;
720
721 constexpr variant_impl& operator=(variant_impl&& rhs) noexcept(
722 traits<T>::is_nothrow_move_assignable) = default;
723
724 [[nodiscard]] static constexpr size_t index() noexcept { return 0; }
725
726 template <size_t I>
727 [[nodiscard]] constexpr typename traits<T>::reference get() & noexcept {
728 return data_;
729 }
730
731 template <size_t I>
732 [[nodiscard]] constexpr typename traits<T>::const_reference get() const& noexcept {
733 return data_;
734 }
735
736 template <size_t I>
737 [[nodiscard]] constexpr typename traits<T>::rvalue_reference get() && {
738 return std::move(data_);
739 }
740
741 template <size_t I>
742 [[nodiscard]] constexpr typename traits<T>::const_rvalue_reference get() const&& {
743 return std::move(data_);
744 }
745
746 template <size_t I>
747 [[nodiscard]] constexpr typename traits<T>::pointer ptr() noexcept {
748 return &data_;
749 }
750
751 template <size_t I>
752 [[nodiscard]] constexpr typename traits<T>::const_pointer ptr() const noexcept {
753 return &data_;
754 }
755
756 template <size_t I, typename... Args>
757 constexpr void emplace(Args&&... args) {
758 // data_ is trivial, so no need to explicitly destroy it
759 ::new (&data_) T(std::forward<Args>(args)...);
760 }
761
762 template <size_t I, typename... Args>
763 constexpr void uninit_emplace(Args&&... args) {
764 ::new (&data_) T(std::forward<Args>(args)...);
765 }
766
767 constexpr void swap(variant_impl& other) noexcept(traits<T>::is_nothrow_swappable) {
768 using std::swap;
769 swap(data_, other.data_);
770 }
771 };
772
773 template <>
774 class variant_impl<void, void> {
775 public:
776 constexpr variant_impl() noexcept = default;
777
778 // NOLINTNEXTLINE(hicpp-explicit-conversions)
779 constexpr variant_impl([[maybe_unused]] uninit_t tag) noexcept : variant_impl() {}
780
781 6 explicit constexpr variant_impl(
782 6 [[maybe_unused]] std::in_place_index_t<0> inplace) noexcept {}
783
784 template <typename T>
785 constexpr variant_impl([[maybe_unused]] std::in_place_index_t<0> inplace,
786 // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
787 [[maybe_unused]] T&& value) noexcept {}
788
789 6 [[nodiscard]] static constexpr size_t index() noexcept { return 0; }
790
791 template <size_t I>
792 static constexpr void get() noexcept {}
793
794 template <size_t I>
795 static constexpr void ptr() noexcept {}
796
797 static constexpr void swap([[maybe_unused]] variant_impl& other) noexcept {}
798 };
799
800 template <typename T>
801 class variant_impl<void, T&, void> {
802 private:
803 T* data_;
804
805 public:
806 variant_impl() = delete;
807
808 // NOLINTNEXTLINE(hicpp-explicit-conversions)
809 constexpr variant_impl([[maybe_unused]] uninit_t tag) noexcept : data_(nullptr) {}
810
811 template <typename U>
812 requires(std::is_convertible_v<U*, T*>)
813 constexpr variant_impl([[maybe_unused]] std::in_place_index_t<0> inplace,
814 U& value) noexcept
815 : data_(&value) {}
816
817 explicit constexpr variant_impl(
818 [[maybe_unused]] std::in_place_index_t<1> inplace) noexcept
819 : data_(nullptr) {}
820
821 template <typename U>
822 explicit constexpr variant_impl([[maybe_unused]] std::in_place_index_t<1> inplace,
823 // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
824 [[maybe_unused]] U&& value) noexcept
825 : data_(nullptr) {}
826
827 [[nodiscard]] constexpr size_t index() const noexcept {
828 return static_cast<size_t>(data_ == nullptr);
829 }
830
831 template <size_t I>
832 [[nodiscard]] constexpr typename traits<select_t<I, T&, void>>::const_reference get()
833 const noexcept {
834 if constexpr (I == 0) {
835 return *data_;
836 } else {
837 return;
838 }
839 }
840
841 template <size_t I>
842 [[nodiscard]] constexpr typename traits<select_t<I, T&, void>>::const_pointer ptr()
843 const noexcept {
844 if constexpr (I == 0) {
845 return data_;
846 } else {
847 return;
848 }
849 }
850
851 template <size_t I>
852 constexpr void emplace() noexcept {
853 static_assert(I == 1, "no matching constructor for reference");
854 data_ = nullptr;
855 }
856
857 template <size_t I, typename U>
858 // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
859 constexpr void emplace([[maybe_unused]] U&& value) noexcept {
860 if constexpr (I == 0) {
861 static_assert(std::is_lvalue_reference_v<U>,
862 "no matching constructor for reference");
863 data_ = &value;
864 } else {
865 data_ = nullptr;
866 }
867 }
868
869 template <size_t I>
870 constexpr void uninit_emplace() noexcept {
871 emplace<I>();
872 }
873
874 template <size_t I, typename U>
875 constexpr void uninit_emplace(U&& value) noexcept {
876 emplace<I>(std::forward<U>(value));
877 }
878
879 constexpr void swap(variant_impl& other) noexcept { std::swap(data_, other.data_); }
880 };
881
882 template <typename T>
883 class variant_impl<void, void, T&> {
884 private:
885 T* data_{nullptr};
886
887 public:
888 10 constexpr variant_impl() noexcept = default;
889
890 // NOLINTNEXTLINE(hicpp-explicit-conversions)
891 2 constexpr variant_impl([[maybe_unused]] uninit_t tag) noexcept : variant_impl() {}
892
893 explicit constexpr variant_impl(
894 [[maybe_unused]] std::in_place_index_t<0> inplace) noexcept
895 : data_(nullptr) {}
896
897 template <typename U>
898 explicit constexpr variant_impl([[maybe_unused]] std::in_place_index_t<0> inplace,
899 // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
900 [[maybe_unused]] U&& value) noexcept
901 : data_(nullptr) {}
902
903 template <typename U>
904 requires(std::is_convertible_v<U*, T*>)
905 2 constexpr variant_impl([[maybe_unused]] std::in_place_index_t<1> inplace,
906 U& value) noexcept
907 2 : data_(&value) {}
908
909 100 [[nodiscard]] constexpr size_t index() const noexcept {
910 100 return static_cast<size_t>(data_ != nullptr);
911 }
912
913 template <size_t I>
914 62 [[nodiscard]] constexpr typename traits<select_t<I, void, T&>>::const_reference get()
915 const noexcept {
916 if constexpr (I == 1) {
917 60 return *data_;
918 } else {
919 2 return;
920 }
921 }
922
923 template <size_t I>
924 [[nodiscard]] constexpr typename traits<select_t<I, void, T&>>::const_pointer ptr()
925 const noexcept {
926 if constexpr (I == 1) {
927 return data_;
928 } else {
929 return;
930 }
931 }
932
933 template <size_t I>
934 1 constexpr void emplace() noexcept {
935 static_assert(I == 0, "no matching constructor for reference");
936 1 data_ = nullptr;
937 1 }
938
939 template <size_t I, typename U>
940 // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
941 8 constexpr void emplace([[maybe_unused]] U&& value) noexcept {
942 if constexpr (I == 1) {
943 static_assert(std::is_lvalue_reference_v<U>,
944 "no matching constructor for reference");
945 8 data_ = &value;
946 } else {
947 data_ = nullptr;
948 }
949 8 }
950
951 template <size_t I>
952 constexpr void uninit_emplace() noexcept {
953 emplace<I>();
954 }
955
956 template <size_t I, typename U>
957 4 constexpr void uninit_emplace(U&& value) noexcept {
958 4 emplace<I>(std::forward<U>(value));
959 4 }
960
961 constexpr void swap(variant_impl& other) noexcept { std::swap(data_, other.data_); }
962 };
963
964 template <typename T, typename U>
965 class variant_impl<std::enable_if_t<is_sumty_empty_v<U>>, T&, U> {
966 private:
967 T* head_;
968 SUMTY_NO_UNIQ_ADDR U tail_{};
969
970 public:
971 variant_impl() = delete;
972
973 // NOLINTNEXTLINE(hicpp-explicit-conversions)
974 constexpr variant_impl([[maybe_unused]] uninit_t tag) noexcept : head_(nullptr) {}
975
976 constexpr variant_impl(const variant_impl& other) noexcept = default;
977
978 constexpr variant_impl(variant_impl&& other) noexcept = default;
979
980 template <typename V>
981 requires(std::is_convertible_v<V*, T*>)
982 constexpr variant_impl([[maybe_unused]] std::in_place_index_t<0> inplace,
983 V& value) noexcept
984 : head_(&value) {}
985
986 template <typename... Args>
987 explicit(sizeof...(Args) == 0)
988 // NOLINTNEXTLINE(hicpp-explicit-conversions)
989 constexpr variant_impl([[maybe_unused]] std::in_place_index_t<1> inplace,
990 Args&&... args)
991 : head_(nullptr), tail_(std::forward<Args>(args)...) {}
992
993 constexpr ~variant_impl() noexcept = default;
994
995 constexpr variant_impl& operator=(const variant_impl& rhs) = default;
996
997 constexpr variant_impl& operator=(variant_impl&& rhs) noexcept = default;
998
999 [[nodiscard]] constexpr size_t index() const noexcept {
1000 return static_cast<size_t>(head_ == nullptr);
1001 }
1002
1003 template <size_t I>
1004 [[nodiscard]] constexpr typename traits<select_t<I, T&, U>>::reference
1005 get() & noexcept {
1006 if constexpr (I == 0) {
1007 return *head_;
1008 } else {
1009 return tail_;
1010 }
1011 }
1012
1013 template <size_t I>
1014 [[nodiscard]] constexpr typename traits<select_t<I, T&, U>>::const_reference get()
1015 const& noexcept {
1016 if constexpr (I == 0) {
1017 return *head_;
1018 } else {
1019 return tail_;
1020 }
1021 }
1022
1023 template <size_t I>
1024 [[nodiscard]] constexpr typename traits<select_t<I, T&, U>>::rvalue_reference get() && {
1025 if constexpr (I == 0) {
1026 return *head_;
1027 } else {
1028 return std::move(tail_);
1029 }
1030 }
1031
1032 template <size_t I>
1033 [[nodiscard]] constexpr typename traits<select_t<I, T&, U>>::const_rvalue_reference
1034 get() const&& {
1035 if constexpr (I == 0) {
1036 return *head_;
1037 } else {
1038 return std::move(tail_);
1039 }
1040 }
1041
1042 template <size_t I>
1043 [[nodiscard]] constexpr typename traits<select_t<I, T&, U>>::pointer ptr() noexcept {
1044 if constexpr (I == 0) {
1045 return head_;
1046 } else {
1047 return &tail_;
1048 }
1049 }
1050
1051 template <size_t I>
1052 [[nodiscard]] constexpr typename traits<select_t<I, T&, U>>::const_pointer ptr()
1053 const noexcept {
1054 if constexpr (I == 0) {
1055 return head_;
1056 } else {
1057 return &tail_;
1058 }
1059 }
1060
1061 template <size_t I, typename... Args>
1062 constexpr void emplace(Args&&... args) noexcept {
1063 if constexpr (I == 0) {
1064 static_assert(
1065 (true && ... && std::is_lvalue_reference_v<Args>)&&sizeof...(Args) == 1,
1066 "no matching constructor for reference");
1067 head_ = std::addressof(std::forward<Args>(args)...);
1068 } else {
1069 head_ = nullptr;
1070 std::construct_at(&tail_, std::forward<Args>(args)...);
1071 }
1072 }
1073
1074 template <size_t I, typename... Args>
1075 constexpr void uninit_emplace(Args&&... args) noexcept {
1076 if constexpr (I == 0) {
1077 static_assert(
1078 (true && ... && std::is_lvalue_reference_v<Args>)&&sizeof...(Args) == 1,
1079 "no matching constructor for reference");
1080 head_ = std::addressof(std::forward<Args>(args)...);
1081 } else {
1082 head_ = nullptr;
1083 std::construct_at(&tail_, std::forward<Args>(args)...);
1084 }
1085 }
1086
1087 constexpr void swap(variant_impl& other) noexcept(
1088 std::is_nothrow_swappable_v<U> && std::is_nothrow_move_constructible_v<U> &&
1089 std::is_nothrow_destructible_v<U>) {
1090 if (head_ == nullptr) {
1091 if (other.head_ != nullptr) {
1092 head_ = other.head_;
1093 other.head_ = nullptr;
1094 }
1095 } else {
1096 if (other.head_ == nullptr) {
1097 other.head_ = head_;
1098 head_ = nullptr;
1099 }
1100 }
1101 }
1102 };
1103
1104 template <typename T, typename U>
1105 class variant_impl<std::enable_if_t<is_sumty_empty_v<T>>, T, U&> {
1106 private:
1107 U* tail_{nullptr};
1108 SUMTY_NO_UNIQ_ADDR T head_{};
1109
1110 public:
1111 constexpr variant_impl() noexcept = default;
1112
1113 // NOLINTNEXTLINE(hicpp-explicit-conversions)
1114 constexpr variant_impl([[maybe_unused]] uninit_t tag) noexcept : tail_(nullptr) {}
1115
1116 constexpr variant_impl(const variant_impl& other) noexcept = default;
1117
1118 constexpr variant_impl(variant_impl&& other) noexcept = default;
1119
1120 template <typename... Args>
1121 explicit(sizeof...(Args) == 0)
1122 // NOLINTNEXTLINE(hicpp-explicit-conversions)
1123 constexpr variant_impl([[maybe_unused]] std::in_place_index_t<0> inplace,
1124 Args&&... args) {
1125 std::construct_at(&head_, std::forward<Args>(args)...);
1126 }
1127
1128 template <typename V>
1129 requires(std::is_convertible_v<V*, U*>)
1130 constexpr variant_impl([[maybe_unused]] std::in_place_index_t<1> inplace,
1131 V& value) noexcept
1132 : tail_(&value) {}
1133
1134 constexpr ~variant_impl() noexcept = default;
1135
1136 constexpr variant_impl& operator=(const variant_impl& rhs) = default;
1137
1138 constexpr variant_impl& operator=(variant_impl&& rhs) noexcept = default;
1139
1140 [[nodiscard]] constexpr size_t index() const noexcept {
1141 return static_cast<size_t>(tail_ != nullptr);
1142 }
1143
1144 template <size_t I>
1145 [[nodiscard]] constexpr typename traits<select_t<I, T, U&>>::reference
1146 get() & noexcept {
1147 if constexpr (I == 0) {
1148 return head_;
1149 } else {
1150 return *tail_;
1151 }
1152 }
1153
1154 template <size_t I>
1155 [[nodiscard]] constexpr typename traits<select_t<I, T, U&>>::const_reference get()
1156 const& noexcept {
1157 if constexpr (I == 0) {
1158 return head_;
1159 } else {
1160 return *tail_;
1161 }
1162 }
1163
1164 template <size_t I>
1165 [[nodiscard]] constexpr typename traits<select_t<I, T, U&>>::rvalue_reference get() && {
1166 if constexpr (I == 0) {
1167 return std::move(head_);
1168 } else {
1169 return *tail_;
1170 }
1171 }
1172
1173 template <size_t I>
1174 [[nodiscard]] constexpr typename traits<select_t<I, T, U&>>::const_rvalue_reference
1175 get() const&& {
1176 if constexpr (I == 0) {
1177 return std::move(head_);
1178 } else {
1179 return *tail_;
1180 }
1181 }
1182
1183 template <size_t I>
1184 [[nodiscard]] constexpr typename traits<select_t<I, T, U&>>::pointer ptr() noexcept {
1185 if constexpr (I == 0) {
1186 return &head_;
1187 } else {
1188 return tail_;
1189 }
1190 }
1191
1192 template <size_t I>
1193 [[nodiscard]] constexpr typename traits<select_t<I, T, U&>>::const_pointer ptr()
1194 const noexcept {
1195 if constexpr (I == 0) {
1196 return &head_;
1197 } else {
1198 return tail_;
1199 }
1200 }
1201
1202 template <size_t I, typename... Args>
1203 constexpr void emplace(Args&&... args) noexcept {
1204 if constexpr (I == 1) {
1205 static_assert(
1206 (true && ... && std::is_lvalue_reference_v<Args>)&&sizeof...(Args) == 1,
1207 "no matching constructor for reference");
1208 tail_ = std::addressof(std::forward<Args>(args)...);
1209 } else {
1210 tail_ = nullptr;
1211 std::construct_at(&head_, std::forward<Args>(args)...);
1212 }
1213 }
1214
1215 template <size_t I, typename... Args>
1216 constexpr void uninit_emplace(Args&&... args) noexcept {
1217 if constexpr (I == 1) {
1218 static_assert(
1219 (true && ... && std::is_lvalue_reference_v<Args>)&&sizeof...(Args) == 1,
1220 "no matching constructor for reference");
1221 tail_ = std::addressof(std::forward<Args>(args)...);
1222 } else {
1223 tail_ = nullptr;
1224 std::construct_at(&head_.head, std::forward<Args>(args)...);
1225 }
1226 }
1227
1228 constexpr void swap(variant_impl& other) noexcept(
1229 std::is_nothrow_swappable_v<T> && std::is_nothrow_move_constructible_v<T> &&
1230 std::is_nothrow_destructible_v<T>) {
1231 if (tail_ == nullptr) {
1232 if (other.tail_ != nullptr) {
1233 tail_ = other.tail_;
1234 other.tail_ = nullptr;
1235 }
1236 } else {
1237 if (other.tail_ == nullptr) {
1238 other.tail_ = tail_;
1239 tail_ = nullptr;
1240 }
1241 }
1242 }
1243 };
1244
1245 template <typename V, typename T>
1246 struct variant_impl_prepend;
1247
1248 template <typename T, typename... TN>
1249 struct variant_impl_prepend<variant_impl<void, TN...>, T> {
1250 using type = variant_impl<void, T, TN...>;
1251 };
1252
1253 template <typename... T>
1254 struct variant_impl_except_last;
1255
1256 template <typename T>
1257 struct variant_impl_except_last<T> {
1258 using type = variant_impl<void>;
1259 };
1260
1261 template <typename T0, typename T1, typename... TN>
1262 struct variant_impl_except_last<T0, T1, TN...> {
1263 using type =
1264 typename variant_impl_prepend<typename variant_impl_except_last<T1, TN...>::type,
1265 T0>::type;
1266 };
1267
1268 template <typename... T>
1269 using variant_impl_except_last_t = typename variant_impl_except_last<T...>::type;
1270
1271 template <typename... T>
1272 class variant_impl<
1273 std::enable_if_t<std::is_base_of_v<never, std::remove_cvref_t<last_t<T...>>> &&
1274 (sizeof...(T) > 1) &&
1275 !std::is_base_of_v<never, std::remove_cvref_t<first_t<T...>>>>,
1276 T...> : variant_impl_except_last_t<T...> {
1277 private:
1278 using inner_t = variant_impl_except_last_t<T...>;
1279 using never_t = last_t<T...>;
1280
1281 18 [[nodiscard]] constexpr inner_t& inner() noexcept {
1282 18 return *static_cast<inner_t*>(this);
1283 }
1284
1285 28 [[nodiscard]] constexpr const inner_t& inner() const noexcept {
1286 28 return *static_cast<const inner_t*>(this);
1287 }
1288
1289 public:
1290 // NOLINTNEXTLINE(hicpp-explicit-conversions)
1291 constexpr variant_impl([[maybe_unused]] uninit_t tag) noexcept {}
1292
1293 2 constexpr variant_impl() noexcept(std::is_nothrow_default_constructible_v<inner_t>) =
1294 default;
1295
1296 1 constexpr variant_impl(const variant_impl&) = default;
1297
1298 1 constexpr variant_impl(variant_impl&&) noexcept(
1299 std::is_nothrow_move_constructible_v<inner_t>) = default;
1300
1301 template <typename... Args>
1302 constexpr explicit(sizeof...(Args) == 0)
1303 // NOLINTNEXTLINE(hicpp-explicit-conversions)
1304 variant_impl([[maybe_unused]] std::in_place_index_t<sizeof...(T)> inplace,
1305 Args&&... args) noexcept {
1306 [[maybe_unused]] never_t value{std::forward<Args>(args)...};
1307 }
1308
1309 template <size_t I, typename... Args>
1310 requires(I < sizeof...(T) - 1)
1311 constexpr explicit(sizeof...(Args) == 0)
1312 // NOLINTNEXTLINE(hicpp-explicit-conversions)
1313 16 variant_impl(std::in_place_index_t<I> inplace, Args&&... args) noexcept(
1314 std::is_nothrow_constructible_v<inner_t, std::in_place_index_t<I>, Args&&...>)
1315 16 : inner_t(inplace, std::forward<Args>(args)...) {}
1316
1317 22 constexpr ~variant_impl() noexcept(std::is_nothrow_destructible_v<inner_t>) = default;
1318
1319 1 constexpr variant_impl& operator=(const variant_impl&) = default;
1320
1321 1 constexpr variant_impl& operator=(variant_impl&&) noexcept(
1322 std::is_nothrow_move_assignable_v<inner_t>) = default;
1323
1324 28 [[nodiscard]] constexpr size_t index() const noexcept { return inner().index(); }
1325
1326 template <size_t I>
1327 // NOLINTNEXTLINE(bugprone-exception-escape)
1328 4 [[nodiscard]] constexpr typename traits<select_t<I, T...>>::reference get() & noexcept {
1329 if constexpr (I == sizeof...(T) - 1) {
1330 typename traits<never_t>::pointer ptr = nullptr;
1331 return *ptr;
1332 } else {
1333 4 return inner().template get<I>();
1334 }
1335 }
1336
1337 template <size_t I>
1338 // NOLINTNEXTLINE(bugprone-exception-escape)
1339 [[nodiscard]] constexpr typename traits<select_t<I, T...>>::const_reference get()
1340 const& noexcept {
1341 if constexpr (I == sizeof...(T) - 1) {
1342 typename traits<never_t>::const_pointer ptr = nullptr;
1343 return *ptr;
1344 } else {
1345 return inner().template get<I>();
1346 }
1347 }
1348
1349 template <size_t I>
1350 [[nodiscard]] constexpr typename traits<select_t<I, T...>>::rvalue_reference
1351 // NOLINTNEXTLINE(bugprone-exception-escape)
1352 10 get() && noexcept {
1353 if constexpr (I == sizeof...(T) - 1) {
1354 typename traits<never_t>::pointer ptr = nullptr;
1355 return std::move(*ptr);
1356 } else {
1357 10 return std::move(inner()).template get<I>();
1358 }
1359 }
1360
1361 template <size_t I>
1362 // NOLINTNEXTLINE(bugprone-exception-escape)
1363 [[nodiscard]] constexpr typename traits<select_t<I, T...>>::const_rvalue_reference get()
1364 const&& noexcept {
1365 if constexpr (I == sizeof...(T) - 1) {
1366 typename traits<never_t>::const_pointer ptr = nullptr;
1367 return std::move(*ptr);
1368 } else {
1369 return std::move(inner()).template get<I>();
1370 }
1371 }
1372
1373 template <size_t I>
1374 [[nodiscard]] constexpr typename traits<select_t<I, T...>>::pointer ptr() noexcept {
1375 if constexpr (I == sizeof...(T) - 1) {
1376 return nullptr;
1377 } else {
1378 return inner().template ptr<I>();
1379 }
1380 }
1381
1382 template <size_t I>
1383 [[nodiscard]] constexpr typename traits<select_t<I, T...>>::const_pointer ptr()
1384 const noexcept {
1385 if constexpr (I == sizeof...(T) - 1) {
1386 return nullptr;
1387 } else {
1388 return inner().template ptr<I>();
1389 }
1390 }
1391
1392 template <size_t I, typename... Args>
1393 constexpr void emplace(Args&&... args) {
1394 if constexpr (I == sizeof...(T) - 1) {
1395 [[maybe_unused]] never_t value{std::forward<Args>(args)...};
1396 } else {
1397 inner().template emplace<I>(std::forward<Args>(args)...);
1398 }
1399 }
1400
1401 template <size_t I, typename... Args>
1402 constexpr void uninit_emplace(Args&&... args) {
1403 if constexpr (I == sizeof...(T) - 1) {
1404 [[maybe_unused]] never_t value{std::forward<Args>(args)...};
1405 } else {
1406 inner().template uninit_emplace<I>(std::forward<Args>(args)...);
1407 }
1408 }
1409
1410 constexpr void swap(variant_impl& other) noexcept(
1411 std::is_nothrow_swappable_v<inner_t>) {
1412 inner().swap(other.inner());
1413 }
1414 };
1415
1416 template <typename T0, typename... TN>
1417 class variant_impl<std::enable_if_t<std::is_base_of_v<never, std::remove_cvref_t<T0>> &&
1418 (sizeof...(TN) > 0)>,
1419 T0,
1420 TN...> : variant_impl<void, TN...> {
1421 private:
1422 37 [[nodiscard]] constexpr variant_impl<void, TN...>& inner() noexcept {
1423 37 return *static_cast<variant_impl<void, TN...>*>(this);
1424 }
1425
1426 55 [[nodiscard]] constexpr const variant_impl<void, TN...>& inner() const noexcept {
1427 55 return *static_cast<const variant_impl<void, TN...>*>(this);
1428 }
1429
1430 public:
1431 static inline constexpr bool okie_dokie = true;
1432
1433 // NOLINTNEXTLINE(hicpp-explicit-conversions)
1434 constexpr variant_impl([[maybe_unused]] uninit_t tag) noexcept {}
1435
1436 constexpr variant_impl() noexcept(
1437 std::is_nothrow_default_constructible_v<variant_impl<void, TN...>>) = default;
1438
1439 1 constexpr variant_impl(const variant_impl&) = default;
1440
1441 1 constexpr variant_impl(variant_impl&&) noexcept(
1442 std::is_nothrow_move_constructible_v<variant_impl<void, TN...>>) = default;
1443
1444 template <typename... Args>
1445 constexpr explicit(sizeof...(Args) == 0)
1446 // NOLINTNEXTLINE(hicpp-explicit-conversions)
1447 variant_impl([[maybe_unused]] std::in_place_index_t<0> inplace,
1448 Args&&... args) noexcept {
1449 [[maybe_unused]] T0 value{std::forward<Args>(args)...};
1450 }
1451
1452 template <size_t I, typename... Args>
1453 requires(I > 0)
1454 // NOLINTNEXTLINE(hicpp-explicit-conversions)
1455 47 constexpr explicit(sizeof...(Args) == 0) variant_impl(
1456 [[maybe_unused]] std::in_place_index_t<I> inplace,
1457 Args&&... args) noexcept(std::is_nothrow_constructible_v<variant_impl<void, TN...>,
1458 std::in_place_index_t<I -
1459 1>,
1460 Args&&...>)
1461 : variant_impl<void, TN...>(std::in_place_index<I - 1>,
1462 47 std::forward<Args>(args)...) {}
1463
1464 41 constexpr ~variant_impl() noexcept(
1465 std::is_nothrow_destructible_v<variant_impl<void, TN...>>) = default;
1466
1467 1 constexpr variant_impl& operator=(const variant_impl&) = default;
1468
1469 1 constexpr variant_impl& operator=(variant_impl&&) noexcept(
1470 std::is_nothrow_move_assignable_v<variant_impl<void, TN...>>) = default;
1471
1472 55 [[nodiscard]] constexpr size_t index() const noexcept { return inner().index() + 1; }
1473
1474 template <size_t I>
1475 [[nodiscard]] constexpr typename traits<select_t<I, T0, TN...>>::reference
1476 // NOLINTNEXTLINE(bugprone-exception-escape)
1477 4 get() & noexcept {
1478 if constexpr (I == 0) {
1479 typename traits<T0>::pointer ptr = nullptr;
1480 return *ptr;
1481 } else {
1482 4 return inner().template get<I - 1>();
1483 }
1484 }
1485
1486 template <size_t I>
1487 // NOLINTNEXTLINE(bugprone-exception-escape)
1488 [[nodiscard]] constexpr typename traits<select_t<I, T0, TN...>>::const_reference get()
1489 const& noexcept {
1490 if constexpr (I == 0) {
1491 typename traits<T0>::const_pointer ptr = nullptr;
1492 return *ptr;
1493 } else {
1494 return inner().template get<I - 1>();
1495 }
1496 }
1497
1498 template <size_t I>
1499 [[nodiscard]] constexpr typename traits<select_t<I, T0, TN...>>::rvalue_reference
1500 // NOLINTNEXTLINE(bugprone-exception-escape)
1501 15 get() && noexcept {
1502 if constexpr (I == 0) {
1503 typename traits<T0>::pointer ptr = nullptr;
1504 return std::move(*ptr);
1505 } else {
1506 15 return std::move(inner()).template get<I - 1>();
1507 }
1508 }
1509
1510 template <size_t I>
1511 [[nodiscard]] constexpr typename traits<select_t<I, T0, TN...>>::const_rvalue_reference
1512 // NOLINTNEXTLINE(bugprone-exception-escape)
1513 get() const&& noexcept {
1514 if constexpr (I == 0) {
1515 typename traits<T0>::const_pointer ptr = nullptr;
1516 return std::move(*ptr);
1517 } else {
1518 return std::move(inner()).template get<I - 1>();
1519 }
1520 }
1521
1522 template <size_t I>
1523 [[nodiscard]] constexpr typename traits<select_t<I, T0, TN...>>::pointer
1524 ptr() noexcept {
1525 if constexpr (I == 0) {
1526 return nullptr;
1527 } else {
1528 return inner().template ptr<I - 1>();
1529 }
1530 }
1531
1532 template <size_t I>
1533 [[nodiscard]] constexpr typename traits<select_t<I, T0, TN...>>::const_pointer ptr()
1534 const noexcept {
1535 if constexpr (I == 0) {
1536 return nullptr;
1537 } else {
1538 return inner().template ptr<I - 1>();
1539 }
1540 }
1541
1542 template <size_t I, typename... Args>
1543 constexpr void emplace(Args&&... args) {
1544 if constexpr (I == 0) {
1545 [[maybe_unused]] T0 value{std::forward<Args>(args)...};
1546 } else {
1547 inner().template emplace<I - 1>(std::forward<Args>(args)...);
1548 }
1549 }
1550
1551 template <size_t I, typename... Args>
1552 constexpr void uninit_emplace(Args&&... args) {
1553 if constexpr (I == 0) {
1554 [[maybe_unused]] T0 value{std::forward<Args>(args)...};
1555 } else {
1556 inner().template uninit_emplace<I - 1>(std::forward<Args>(args)...);
1557 }
1558 }
1559
1560 constexpr void swap(variant_impl& other) noexcept(
1561 std::is_nothrow_swappable_v<variant_impl<void, TN...>>) {
1562 inner().swap(other.inner());
1563 }
1564 };
1565
1566 } // namespace sumty::detail
1567
1568 #ifdef _MSC_VER
1569 #pragma warning(pop)
1570 #endif
1571
1572 #endif
1573