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 |