1 //===----------------------------------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // MSVC warning C4244: 'argument': conversion from '_Ty' to 'int', possible loss of data
10 // ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd4244
12 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
14 // template<class C, input_range R, class... Args> requires (!view<C>)
15 // constexpr C to(R&& r, Args&&... args); // Since C++23
23 #include "container.h"
24 #include "test_iterators.h"
25 #include "test_macros.h"
26 #include "test_range.h"
28 template <class Container
, class Range
, class... Args
>
29 concept HasTo
= requires (Range
&& range
, Args
...args
) {
30 std::ranges::to
<Container
>(std::forward
<Range
>(range
), std::forward
<Args
>(args
)...);
35 constexpr cpp20_input_iterator
<int*> begin() {
36 return cpp20_input_iterator
<int*>(&x
);
38 constexpr sentinel_wrapper
<cpp20_input_iterator
<int*>> end() {
39 return sentinel_wrapper
<cpp20_input_iterator
<int*>>(begin());
42 static_assert(std::ranges::input_range
<InputRange
>);
44 struct common_cpp20_input_iterator
{
45 using value_type
= int;
46 using difference_type
= long long;
47 using iterator_concept
= std::input_iterator_tag
;
48 // Deliberately not defining `iterator_category` to make sure this class satisfies the `input_iterator` concept but
49 // would fail `derived_from<iterator_category, input_iterator_tag>`.
53 // Copyable so that it can be used as a sentinel against itself.
54 constexpr decltype(auto) operator*() const { return x
; }
55 constexpr common_cpp20_input_iterator
& operator++() { return *this; }
56 constexpr void operator++(int) {}
57 constexpr friend bool operator==(common_cpp20_input_iterator
, common_cpp20_input_iterator
) { return true; }
59 static_assert(std::input_iterator
<common_cpp20_input_iterator
>);
60 static_assert(std::sentinel_for
<common_cpp20_input_iterator
, common_cpp20_input_iterator
>);
62 concept HasIteratorCategory
= requires
{
63 typename
std::iterator_traits
<T
>::iterator_category
;
65 static_assert(!HasIteratorCategory
<common_cpp20_input_iterator
>);
67 struct CommonInputRange
{
69 constexpr common_cpp20_input_iterator
begin() { return {}; }
70 constexpr common_cpp20_input_iterator
end() { return begin(); }
72 static_assert(std::ranges::input_range
<CommonInputRange
>);
73 static_assert(std::ranges::common_range
<CommonInputRange
>);
77 constexpr forward_iterator
<int*> begin() {
78 return forward_iterator
<int*>(&x
);
80 constexpr forward_iterator
<int*> end() {
84 static_assert(std::ranges::input_range
<CommonRange
>);
85 static_assert(std::ranges::common_range
<CommonRange
>);
87 struct NonCommonRange
{
89 constexpr forward_iterator
<int*> begin() {
90 return forward_iterator
<int*>(&x
);
92 constexpr sentinel_wrapper
<forward_iterator
<int*>> end() {
93 return sentinel_wrapper
<forward_iterator
<int*>>(begin());
96 static_assert(std::ranges::input_range
<NonCommonRange
>);
97 static_assert(!std::ranges::common_range
<NonCommonRange
>);
98 static_assert(std::derived_from
<
99 typename
std::iterator_traits
<std::ranges::iterator_t
<NonCommonRange
>>::iterator_category
,
100 std::input_iterator_tag
>);
102 using ContainerT
= int;
103 static_assert(!std::ranges::view
<ContainerT
>);
104 static_assert(HasTo
<ContainerT
, InputRange
>);
105 static_assert(!HasTo
<test_view
<forward_iterator
>, InputRange
>);
107 // Note: it's not possible to check the `input_range` constraint because if it's not satisfied, the pipe adaptor
108 // overload hijacks the call (it takes unconstrained variadic arguments).
110 // Check the exact constraints for each one of the cases inside `ranges::to`.
115 using value_type
= int;
117 CtrChoice ctr_choice
= CtrChoice::Invalid
;
120 constexpr Fallback() : ctr_choice(CtrChoice::DefaultCtrAndInsert
) {}
121 constexpr Fallback(Empty
) : ctr_choice(CtrChoice::DefaultCtrAndInsert
) {}
123 constexpr void push_back(value_type
) {}
124 constexpr value_type
* begin() { return &x
; }
125 constexpr value_type
* end() { return &x
; }
126 std::size_t size() const { return 0; }
129 struct CtrDirectOrFallback
: Fallback
{
130 using Fallback::Fallback
;
131 constexpr CtrDirectOrFallback(InputRange
&&, int = 0) { ctr_choice
= CtrChoice::DirectCtr
; }
134 struct CtrFromRangeTOrFallback
: Fallback
{
135 using Fallback::Fallback
;
136 constexpr CtrFromRangeTOrFallback(std::from_range_t
, InputRange
&&, int = 0) { ctr_choice
= CtrChoice::FromRangeT
; }
139 struct CtrBeginEndPairOrFallback
: Fallback
{
140 using Fallback::Fallback
;
141 template <class Iter
>
142 constexpr CtrBeginEndPairOrFallback(Iter
, Iter
, int = 0) { ctr_choice
= CtrChoice::BeginEndPair
; }
145 template <bool HasSize
>
146 struct MaybeSizedRange
{
148 constexpr forward_iterator
<int*> begin() { return forward_iterator
<int*>(&x
); }
149 constexpr forward_iterator
<int*> end() { return begin(); }
151 constexpr std::size_t size() const
156 static_assert(std::ranges::sized_range
<MaybeSizedRange
<true>>);
157 static_assert(!std::ranges::sized_range
<MaybeSizedRange
<false>>);
159 template <bool HasCapacity
= true, bool CapacityReturnsSizeT
= true,
160 bool HasMaxSize
= true, bool MaxSizeReturnsSizeT
= true>
161 struct Reservable
: Fallback
{
162 bool reserve_called
= false;
164 using Fallback::Fallback
;
166 constexpr std::size_t capacity() const
167 requires (HasCapacity
&& CapacityReturnsSizeT
) {
170 constexpr int capacity() const
171 requires (HasCapacity
&& !CapacityReturnsSizeT
) {
175 constexpr std::size_t max_size() const
176 requires (HasMaxSize
&& MaxSizeReturnsSizeT
) {
179 constexpr int max_size() const
180 requires (HasMaxSize
&& !MaxSizeReturnsSizeT
) {
184 constexpr void reserve(std::size_t) {
185 reserve_called
= true;
188 LIBCPP_STATIC_ASSERT(std::ranges::__reservable_container
<Reservable
<>>);
190 constexpr void test_constraints() {
191 { // Case 1 -- construct directly from the range.
193 auto result
= std::ranges::to
<CtrDirectOrFallback
>(InputRange());
194 assert(result
.ctr_choice
== CtrChoice::DirectCtr
);
198 auto result
= std::ranges::to
<CtrDirectOrFallback
>(InputRange(), 1);
199 assert(result
.ctr_choice
== CtrChoice::DirectCtr
);
202 { // (range, convertible-to-arg)
203 auto result
= std::ranges::to
<CtrDirectOrFallback
>(InputRange(), 1.0);
204 assert(result
.ctr_choice
== CtrChoice::DirectCtr
);
207 { // (range, BAD_arg)
208 auto result
= std::ranges::to
<CtrDirectOrFallback
>(InputRange(), Empty());
209 assert(result
.ctr_choice
== CtrChoice::DefaultCtrAndInsert
);
213 { // Case 2 -- construct using the `from_range_t` tagged constructor.
215 auto result
= std::ranges::to
<CtrFromRangeTOrFallback
>(InputRange());
216 assert(result
.ctr_choice
== CtrChoice::FromRangeT
);
220 auto result
= std::ranges::to
<CtrFromRangeTOrFallback
>(InputRange(), 1);
221 assert(result
.ctr_choice
== CtrChoice::FromRangeT
);
224 { // (range, convertible-to-arg)
225 auto result
= std::ranges::to
<CtrFromRangeTOrFallback
>(InputRange(), 1.0);
226 assert(result
.ctr_choice
== CtrChoice::FromRangeT
);
229 { // (range, BAD_arg)
230 auto result
= std::ranges::to
<CtrFromRangeTOrFallback
>(InputRange(), Empty());
231 assert(result
.ctr_choice
== CtrChoice::DefaultCtrAndInsert
);
235 { // Case 3 -- construct from a begin-end iterator pair.
237 auto result
= std::ranges::to
<CtrBeginEndPairOrFallback
>(CommonRange());
238 assert(result
.ctr_choice
== CtrChoice::BeginEndPair
);
242 auto result
= std::ranges::to
<CtrBeginEndPairOrFallback
>(CommonRange(), 1);
243 assert(result
.ctr_choice
== CtrChoice::BeginEndPair
);
246 { // (range, convertible-to-arg)
247 auto result
= std::ranges::to
<CtrBeginEndPairOrFallback
>(CommonRange(), 1.0);
248 assert(result
.ctr_choice
== CtrChoice::BeginEndPair
);
251 { // (BAD_range) -- not a common range.
252 auto result
= std::ranges::to
<CtrBeginEndPairOrFallback
>(NonCommonRange());
253 assert(result
.ctr_choice
== CtrChoice::DefaultCtrAndInsert
);
256 { // (BAD_range) -- iterator type not derived from `input_iterator_tag`.
257 auto result
= std::ranges::to
<CtrBeginEndPairOrFallback
>(CommonInputRange());
258 assert(result
.ctr_choice
== CtrChoice::DefaultCtrAndInsert
);
261 { // (range, BAD_arg)
262 auto result
= std::ranges::to
<CtrBeginEndPairOrFallback
>(CommonRange(), Empty());
263 assert(result
.ctr_choice
== CtrChoice::DefaultCtrAndInsert
);
267 { // Case 4 -- default-construct (or construct from the extra arguments) and insert, reserving the size if possible.
268 // Note: it's not possible to check the constraints on the default constructor using this approach because there is
269 // nothing to fall back to -- the call will result in a hard error.
270 // However, it's possible to check the constraints on reserving the capacity.
272 { // All constraints satisfied.
273 using C
= Reservable
<>;
274 auto result
= std::ranges::to
<C
>(MaybeSizedRange
<true>());
275 assert(result
.reserve_called
);
279 using C
= Reservable
<>;
280 auto result
= std::ranges::to
<C
>(MaybeSizedRange
<false>());
281 assert(!result
.reserve_called
);
284 { // Missing `capacity`.
285 using C
= Reservable
</*HasCapacity=*/false>;
286 auto result
= std::ranges::to
<C
>(MaybeSizedRange
<true>());
287 assert(!result
.reserve_called
);
290 { // `capacity` doesn't return `size_type`.
291 using C
= Reservable
</*HasCapacity=*/true, /*CapacityReturnsSizeT=*/false>;
292 auto result
= std::ranges::to
<C
>(MaybeSizedRange
<true>());
293 assert(!result
.reserve_called
);
296 { // Missing `max_size`.
297 using C
= Reservable
</*HasCapacity=*/true, /*CapacityReturnsSizeT=*/true, /*HasMaxSize=*/false>;
298 auto result
= std::ranges::to
<C
>(MaybeSizedRange
<true>());
299 assert(!result
.reserve_called
);
302 { // `max_size` doesn't return `size_type`.
303 using C
= Reservable
<
304 /*HasCapacity=*/true, /*CapacityReturnsSizeT=*/true, /*HasMaxSize=*/true, /*MaxSizeReturnsSizeT=*/false>;
305 auto result
= std::ranges::to
<C
>(MaybeSizedRange
<true>());
306 assert(!result
.reserve_called
);
311 constexpr void test_ctr_choice_order() {
312 std::array in
= {1, 2, 3, 4, 5};
316 { // Case 1 -- construct directly from the given range.
318 using C
= Container
<int, CtrChoice::DirectCtr
>;
319 std::same_as
<C
> decltype(auto) result
= std::ranges::to
<C
>(in
);
321 assert(result
.ctr_choice
== CtrChoice::DirectCtr
);
322 assert(std::ranges::equal(result
, in
));
323 assert((in
| std::ranges::to
<C
>()) == result
);
324 auto closure
= std::ranges::to
<C
>();
325 assert((in
| closure
) == result
);
328 { // Extra arguments.
329 using C
= Container
<int, CtrChoice::DirectCtr
>;
330 std::same_as
<C
> decltype(auto) result
= std::ranges::to
<C
>(in
, arg1
, arg2
);
332 assert(result
.ctr_choice
== CtrChoice::DirectCtr
);
333 assert(std::ranges::equal(result
, in
));
334 assert(result
.extra_arg1
== arg1
);
335 assert(result
.extra_arg2
== arg2
);
336 assert((in
| std::ranges::to
<C
>(arg1
, arg2
)) == result
);
337 auto closure
= std::ranges::to
<C
>(arg1
, arg2
);
338 assert((in
| closure
) == result
);
342 { // Case 2 -- construct using the `from_range_t` tag.
344 using C
= Container
<int, CtrChoice::FromRangeT
>;
345 std::same_as
<C
> decltype(auto) result
= std::ranges::to
<C
>(in
);
347 assert(result
.ctr_choice
== CtrChoice::FromRangeT
);
348 assert(std::ranges::equal(result
, in
));
349 assert((in
| std::ranges::to
<C
>()) == result
);
350 auto closure
= std::ranges::to
<C
>();
351 assert((in
| closure
) == result
);
354 { // Extra arguments.
355 using C
= Container
<int, CtrChoice::FromRangeT
>;
356 std::same_as
<C
> decltype(auto) result
= std::ranges::to
<C
>(in
, arg1
, arg2
);
358 assert(result
.ctr_choice
== CtrChoice::FromRangeT
);
359 assert(std::ranges::equal(result
, in
));
360 assert(result
.extra_arg1
== arg1
);
361 assert(result
.extra_arg2
== arg2
);
362 assert((in
| std::ranges::to
<C
>(arg1
, arg2
)) == result
);
363 auto closure
= std::ranges::to
<C
>(arg1
, arg2
);
364 assert((in
| closure
) == result
);
368 { // Case 3 -- construct from a begin-end pair.
370 using C
= Container
<int, CtrChoice::BeginEndPair
>;
371 std::same_as
<C
> decltype(auto) result
= std::ranges::to
<C
>(in
);
373 assert(result
.ctr_choice
== CtrChoice::BeginEndPair
);
374 assert(std::ranges::equal(result
, in
));
375 assert((in
| std::ranges::to
<C
>()) == result
);
376 auto closure
= std::ranges::to
<C
>();
377 assert((in
| closure
) == result
);
380 { // Extra arguments.
381 using C
= Container
<int, CtrChoice::BeginEndPair
>;
382 std::same_as
<C
> decltype(auto) result
= std::ranges::to
<C
>(in
, arg1
, arg2
);
384 assert(result
.ctr_choice
== CtrChoice::BeginEndPair
);
385 assert(std::ranges::equal(result
, in
));
386 assert(result
.extra_arg1
== arg1
);
387 assert(result
.extra_arg2
== arg2
);
388 assert((in
| std::ranges::to
<C
>(arg1
, arg2
)) == result
);
389 auto closure
= std::ranges::to
<C
>(arg1
, arg2
);
390 assert((in
| closure
) == result
);
394 { // Case 4 -- default-construct then insert elements.
395 auto case_4
= [in
, arg1
, arg2
]<auto InserterChoice
, bool CanReserve
>() {
396 using C
= Container
<int, CtrChoice::DefaultCtrAndInsert
, InserterChoice
, CanReserve
>;
398 [[maybe_unused
]] std::same_as
<C
> decltype(auto) result
= std::ranges::to
<C
>(in
);
400 assert(result
.ctr_choice
== CtrChoice::DefaultCtrAndInsert
);
401 assert(result
.inserter_choice
== InserterChoice
);
402 assert(std::ranges::equal(result
, in
));
404 if constexpr (CanReserve
) {
405 assert(result
.called_reserve
);
407 assert(!result
.called_reserve
);
410 assert((in
| std::ranges::to
<C
>()) == result
);
411 [[maybe_unused
]] auto closure
= std::ranges::to
<C
>();
412 assert((in
| closure
) == result
);
416 [[maybe_unused
]] std::same_as
<C
> decltype(auto) result
= std::ranges::to
<C
>(in
, arg1
, arg2
);
418 assert(result
.ctr_choice
== CtrChoice::DefaultCtrAndInsert
);
419 assert(result
.inserter_choice
== InserterChoice
);
420 assert(std::ranges::equal(result
, in
));
421 assert(result
.extra_arg1
== arg1
);
422 assert(result
.extra_arg2
== arg2
);
424 if constexpr (CanReserve
) {
425 assert(result
.called_reserve
);
427 assert(!result
.called_reserve
);
430 assert((in
| std::ranges::to
<C
>(arg1
, arg2
)) == result
);
431 [[maybe_unused
]] auto closure
= std::ranges::to
<C
>(arg1
, arg2
);
432 assert((in
| closure
) == result
);
436 case_4
.operator()<InserterChoice::Insert
, false>();
437 case_4
.operator()<InserterChoice::Insert
, true>();
438 case_4
.operator()<InserterChoice::Emplace
, false>();
439 case_4
.operator()<InserterChoice::Emplace
, true>();
440 case_4
.operator()<InserterChoice::PushBack
, false>();
441 case_4
.operator()<InserterChoice::PushBack
, true>();
442 case_4
.operator()<InserterChoice::EmplaceBack
, false>();
443 case_4
.operator()<InserterChoice::EmplaceBack
, true>();
447 template <CtrChoice Rank
>
449 using value_type
= int;
451 constexpr NotARange(std::ranges::input_range
auto&&)
452 requires (Rank
>= CtrChoice::DirectCtr
)
455 constexpr NotARange(std::from_range_t
, std::ranges::input_range
auto&&)
456 requires (Rank
>= CtrChoice::FromRangeT
)
459 template <class Iter
>
460 constexpr NotARange(Iter
, Iter
)
461 requires (Rank
>= CtrChoice::BeginEndPair
)
464 constexpr NotARange()
465 requires (Rank
>= CtrChoice::DefaultCtrAndInsert
)
468 constexpr void push_back(int) {}
471 static_assert(!std::ranges::range
<NotARange
<CtrChoice::DirectCtr
>>);
473 constexpr void test_lwg_3785() {
474 // Test LWG 3785 ("`ranges::to` is over-constrained on the destination type being a range") -- make sure it's possible
475 // to convert the given input range to a non-range type.
476 std::array in
= {1, 2, 3, 4, 5};
479 using C
= NotARange
<CtrChoice::DirectCtr
>;
480 [[maybe_unused
]] std::same_as
<C
> decltype(auto) result
= std::ranges::to
<C
>(in
);
484 using C
= NotARange
<CtrChoice::FromRangeT
>;
485 [[maybe_unused
]] std::same_as
<C
> decltype(auto) result
= std::ranges::to
<C
>(in
);
489 using C
= NotARange
<CtrChoice::BeginEndPair
>;
490 [[maybe_unused
]] std::same_as
<C
> decltype(auto) result
= std::ranges::to
<C
>(in
);
494 using C
= NotARange
<CtrChoice::DefaultCtrAndInsert
>;
495 [[maybe_unused
]] std::same_as
<C
> decltype(auto) result
= std::ranges::to
<C
>(in
);
499 constexpr void test_recursive() {
500 using C1
= Container
<int, CtrChoice::DirectCtr
>;
501 using C2
= Container
<C1
, CtrChoice::FromRangeT
>;
502 using C3
= Container
<C2
, CtrChoice::BeginEndPair
>;
503 using C4
= Container
<C3
, CtrChoice::DefaultCtrAndInsert
, InserterChoice::PushBack
>;
504 using A1
= std::array
<int, 4>;
505 using A2
= std::array
<A1
, 3>;
506 using A3
= std::array
<A2
, 2>;
507 using A4
= std::array
<A3
, 2>;
510 { // Fill the nested array with incremental values.
512 for (auto& a3
: in
) {
513 for (auto& a2
: a3
) {
514 for (auto& a1
: a2
) {
523 std::same_as
<C4
> decltype(auto) result
= std::ranges::to
<C4
>(in
);
525 assert(result
.ctr_choice
== CtrChoice::DefaultCtrAndInsert
);
527 int expected_value
= 0;
528 for (auto& c3
: result
) {
529 assert(c3
.ctr_choice
== CtrChoice::BeginEndPair
);
531 for (auto& c2
: c3
) {
532 assert(c2
.ctr_choice
== CtrChoice::FromRangeT
);
534 for (auto& c1
: c2
) {
535 assert(c1
.ctr_choice
== CtrChoice::DirectCtr
);
538 assert(el
== expected_value
);
545 assert((in
| std::ranges::to
<C4
>()) == result
);
547 // LWG3984: ranges::to's recursion branch may be ill-formed
548 auto in_owning_view
= std::views::all(std::move(in
));
549 static_assert(!std::ranges::viewable_range
<decltype((in_owning_view
))>);
550 assert(std::ranges::to
<C4
>(in_owning_view
) == result
);
553 constexpr bool test() {
555 test_ctr_choice_order();
562 int main(int, char**) {
564 static_assert(test());