[libc++][ranges] Implement ranges::to.

Differential Revision: https://reviews.llvm.org/D142335
This commit is contained in:
varconst 2023-07-14 20:54:38 -07:00 committed by Konstantin Varlamov
parent 7baf5d3841
commit c3648f37d0
32 changed files with 1645 additions and 283 deletions

View File

@ -358,7 +358,7 @@ Status
--------------------------------------------------- -----------------
``__cpp_lib_ranges_starts_ends_with`` *unimplemented*
--------------------------------------------------- -----------------
``__cpp_lib_ranges_to_container`` *unimplemented*
``__cpp_lib_ranges_to_container`` ``202202L``
--------------------------------------------------- -----------------
``__cpp_lib_ranges_zip`` *unimplemented*
--------------------------------------------------- -----------------

View File

@ -55,6 +55,7 @@ Work has started on the C++17 Parallel STL. This feature is experimental, see
Implemented Papers
------------------
- P1206R7 - ``ranges::to``: A function to convert any range to a container
- P2520R0 - ``move_iterator<T*>`` should be a random access iterator
- P1328R1 - ``constexpr type_info::operator==()``
- P1413R3 - Formatting ``thread::id`` (the ``stacktrace`` is not done yet)

View File

@ -258,7 +258,7 @@
"`3820 <https://wg21.link/LWG3820>`__","``cartesian_product_view::iterator::prev`` is not quite right","February 2023","","","|ranges|"
"`3825 <https://wg21.link/LWG3825>`__","Missing compile-time argument ``id`` check in ``basic_format_parse_context::next_arg_id``","February 2023","|Complete|","17.0","|format|"
"`3204 <https://wg21.link/LWG3204>`__","``sub_match::swap`` only swaps the base class","February 2023","|Complete|","17.0",""
"`3733 <https://wg21.link/LWG3733>`__","``ranges::to`` misuses ``cpp17-input-iterator``","February 2023","","","|ranges|"
"`3733 <https://wg21.link/LWG3733>`__","``ranges::to`` misuses ``cpp17-input-iterator``","February 2023","|Complete|","17.0","|ranges|"
"`3742 <https://wg21.link/LWG3742>`__","``deque::prepend_range`` needs to permute","February 2023","","","|ranges|"
"`3790 <https://wg21.link/LWG3790>`__","`P1467 <https://wg21.link/P1467>`__ accidentally changed ``nexttoward``'s signature","February 2023","","",""
"`3819 <https://wg21.link/LWG3819>`__","``reference_meows_from_temporary`` should not use ``is_meowible``","February 2023","","",""
@ -292,7 +292,7 @@
"`3833 <https://wg21.link/LWG3833>`__","Remove specialization ``template<size_t N> struct formatter<const charT[N], charT>``","February 2023","|Complete|","17.0","|format|"
"`3836 <https://wg21.link/LWG3836>`__","``std::expected<bool, E1>`` conversion constructor ``expected(const expected<U, G>&)`` should take precedence over ``expected(U&&)`` with operator ``bool``","February 2023","","",""
"`3843 <https://wg21.link/LWG3843>`__","``std::expected<T,E>::value() &`` assumes ``E`` is copy constructible","February 2023","|Complete|","17.0",""
"`3847 <https://wg21.link/LWG3847>`__","``ranges::to`` can still return views","February 2023","","","|ranges|"
"`3847 <https://wg21.link/LWG3847>`__","``ranges::to`` can still return views","February 2023","|Complete|","17.0","|ranges|"
"`3862 <https://wg21.link/LWG3862>`__","``basic_const_iterator``'s ``common_type`` specialization is underconstrained","February 2023","","",""
"`3865 <https://wg21.link/LWG3865>`__","Sorting a range of ``pairs``","February 2023","|Complete|","17.0","|ranges|"
"`3869 <https://wg21.link/LWG3869>`__","Deprecate ``std::errc`` constants related to UNIX STREAMS","February 2023","","",""

Can't render this file because it has a wrong number of fields in line 2.

View File

@ -41,7 +41,7 @@
"`P0323R12 <https://wg21.link/P0323R12>`__","LWG","``std::expected``","February 2022","|Complete|","16.0"
"`P0533R9 <https://wg21.link/P0533R9>`__","LWG","``constexpr`` for ``<cmath>`` and ``<cstdlib>``","February 2022","|In progress| [#note-P0533R9]_",""
"`P0627R6 <https://wg21.link/P0627R6>`__","LWG","Function to mark unreachable code","February 2022","|Complete|","15.0"
"`P1206R7 <https://wg21.link/P1206R7>`__","LWG","``ranges::to``: A function to convert any range to a container","February 2022","|In Progress|","","|ranges|"
"`P1206R7 <https://wg21.link/P1206R7>`__","LWG","``ranges::to``: A function to convert any range to a container","February 2022","|Complete|","17.0","|ranges|"
"`P1413R3 <https://wg21.link/P1413R3>`__","LWG","Deprecate ``std::aligned_storage`` and ``std::aligned_union``","February 2022","|Complete| [#note-P1413R3]_",""
"`P2255R2 <https://wg21.link/P2255R2>`__","LWG","A type trait to detect reference binding to temporary","February 2022","",""
"`P2273R3 <https://wg21.link/P2273R3>`__","LWG","Making ``std::unique_ptr`` constexpr","February 2022","|Complete|","16.0"

Can't render this file because it has a wrong number of fields in line 2.

View File

@ -1,4 +1,4 @@
Standard,Name,Assignee,CL,Status
C++23,`ranges::to <https://wg21.link/P1206R7>`_,Unassigned,No patch yet,Not started
C++23,`ranges::to <https://wg21.link/P1206R7>`_,Konstantin Varlamov,`D142335 <https://reviews.llvm.org/D142335>`_,Complete
C++23,`Pipe support for user-defined range adaptors <https://wg21.link/P2387R3>`_,Unassigned,No patch yet,Not started
C++23,`Formatting Ranges <https://wg21.link/P2286R8>`_,Mark de Wever,Various,Complete

1 Standard Name Assignee CL Status
2 C++23 `ranges::to <https://wg21.link/P1206R7>`_ Unassigned Konstantin Varlamov No patch yet `D142335 <https://reviews.llvm.org/D142335>`_ Not started Complete
3 C++23 `Pipe support for user-defined range adaptors <https://wg21.link/P2387R3>`_ Unassigned No patch yet Not started
4 C++23 `Formatting Ranges <https://wg21.link/P2286R8>`_ Mark de Wever Various Complete

View File

@ -646,6 +646,7 @@ set(files
__ranges/subrange.h
__ranges/take_view.h
__ranges/take_while_view.h
__ranges/to.h
__ranges/transform_view.h
__ranges/view_interface.h
__ranges/views.h

View File

@ -0,0 +1,247 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef _LIBCPP___RANGES_TO_H
#define _LIBCPP___RANGES_TO_H
#include <__algorithm/ranges_copy.h>
#include <__concepts/constructible.h>
#include <__concepts/convertible_to.h>
#include <__concepts/derived_from.h>
#include <__concepts/same_as.h>
#include <__config>
#include <__functional/bind_back.h>
#include <__iterator/back_insert_iterator.h>
#include <__iterator/insert_iterator.h>
#include <__iterator/iterator_traits.h>
#include <__ranges/access.h>
#include <__ranges/concepts.h>
#include <__ranges/from_range.h>
#include <__ranges/range_adaptor.h>
#include <__ranges/size.h>
#include <__ranges/transform_view.h>
#include <__type_traits/add_pointer.h>
#include <__type_traits/is_const.h>
#include <__type_traits/is_volatile.h>
#include <__type_traits/type_identity.h>
#include <__utility/declval.h>
#include <__utility/forward.h>
#include <cstddef>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
_LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER >= 23
namespace ranges {
// TODO(clang-15): in the Standard, it's a `constexpr bool` variable, not a concept, but constexpr variables don't
// short-circuit properly on Clang 15 (fixed in later versions), so use a concept as a workaround.
template <class _Container>
concept __reservable_container = sized_range<_Container> && requires(_Container& __c, range_size_t<_Container> __n) {
__c.reserve(__n);
{ __c.capacity() } -> same_as<decltype(__n)>;
{ __c.max_size() } -> same_as<decltype(__n)>;
};
template <class _Container, class _Ref>
constexpr bool __container_insertable = requires(_Container& __c, _Ref&& __ref) {
requires(
requires { __c.push_back(std::forward<_Ref>(__ref)); } ||
requires { __c.insert(__c.end(), std::forward<_Ref>(__ref)); });
};
template <class _Ref, class _Container>
_LIBCPP_HIDE_FROM_ABI constexpr auto __container_inserter(_Container& __c) {
if constexpr (requires { __c.push_back(std::declval<_Ref>()); }) {
return std::back_inserter(__c);
} else {
return std::inserter(__c, __c.end());
}
}
// Note: making this a concept allows short-circuiting the second condition.
template <class _Container, class _Range>
concept __try_non_recursive_conversion =
!input_range<_Container> || convertible_to<range_reference_t<_Range>, range_value_t<_Container>>;
template <class _Container, class _Range, class... _Args>
concept __constructible_from_iter_pair =
common_range<_Range> && requires { typename iterator_traits<iterator_t<_Range>>::iterator_category; } &&
derived_from<typename iterator_traits<iterator_t<_Range>>::iterator_category, input_iterator_tag> &&
constructible_from<_Container, iterator_t<_Range>, sentinel_t<_Range>, _Args...>;
template <class>
concept __always_false = false;
// `ranges::to` base template -- the `_Container` type is a simple type template parameter.
template <class _Container, input_range _Range, class... _Args>
requires(!view<_Container>)
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr _Container to(_Range&& __range, _Args&&... __args) {
// Mandates: C is a cv-unqualified class type.
static_assert(!is_const_v<_Container>, "The target container cannot be const-qualified, please remove the const");
static_assert(
!is_volatile_v<_Container>, "The target container cannot be volatile-qualified, please remove the volatile");
// First see if the non-recursive case applies -- the conversion target is either:
// - a range with a convertible value type;
// - a non-range type which might support being created from the input argument(s) (e.g. an `optional`).
if constexpr (__try_non_recursive_conversion<_Container, _Range>) {
// Case 1 -- construct directly from the given range.
if constexpr (constructible_from<_Container, _Range, _Args...>) {
return _Container(std::forward<_Range>(__range), std::forward<_Args>(__args)...);
}
// Case 2 -- construct using the `from_range_t` tagged constructor.
else if constexpr (constructible_from<_Container, from_range_t, _Range, _Args...>) {
return _Container(from_range, std::forward<_Range>(__range), std::forward<_Args>(__args)...);
}
// Case 3 -- construct from a begin-end iterator pair.
else if constexpr (__constructible_from_iter_pair<_Container, _Range, _Args...>) {
return _Container(ranges::begin(__range), ranges::end(__range), std::forward<_Args>(__args)...);
}
// Case 4 -- default-construct (or construct from the extra arguments) and insert, reserving the size if possible.
else if constexpr (constructible_from<_Container, _Args...> &&
__container_insertable<_Container, range_reference_t<_Range>>) {
_Container __result(std::forward<_Args>(__args)...);
if constexpr (sized_range<_Range> && __reservable_container<_Container>) {
__result.reserve(static_cast<range_size_t<_Container>>(ranges::size(__range)));
}
ranges::copy(__range, ranges::__container_inserter<range_reference_t<_Range>>(__result));
return __result;
} else {
static_assert(__always_false<_Container>, "ranges::to: unable to convert to the given container type.");
}
// Try the recursive case.
} else if constexpr (input_range<range_reference_t<_Range>>) {
return ranges::to<_Container>(
__range | views::transform([](auto&& __elem) {
return ranges::to<range_value_t<_Container>>(std::forward<decltype(__elem)>(__elem));
}),
std::forward<_Args>(__args)...);
} else {
static_assert(__always_false<_Container>, "ranges::to: unable to convert to the given container type.");
}
}
template <class _Range>
struct __minimal_input_iterator {
using iterator_category = input_iterator_tag;
using value_type = range_value_t<_Range>;
using difference_type = ptrdiff_t;
using pointer = add_pointer_t<range_reference_t<_Range>>;
using reference = range_reference_t<_Range>;
reference operator*() const;
pointer operator->() const;
__minimal_input_iterator& operator++();
__minimal_input_iterator operator++(int);
bool operator==(const __minimal_input_iterator&) const;
};
// Deduces the full type of the container from the given template template parameter.
template <template <class...> class _Container, input_range _Range, class... _Args>
struct _Deducer {
_LIBCPP_HIDE_FROM_ABI static constexpr auto __deduce_func() {
using _InputIter = __minimal_input_iterator<_Range>;
// Case 1 -- can construct directly from the given range.
if constexpr (requires { _Container(std::declval<_Range>(), std::declval<_Args>()...); }) {
using _Result = decltype( //
_Container(std::declval<_Range>(), std::declval<_Args>()...));
return type_identity<_Result>{};
// Case 2 -- can construct from the given range using the `from_range_t` tagged constructor.
} else if constexpr ( //
requires { _Container(from_range, std::declval<_Range>(), std::declval<_Args>()...); }) {
using _Result = //
decltype(_Container(from_range, std::declval<_Range>(), std::declval<_Args>()...));
return type_identity<_Result>{};
// Case 3 -- can construct from a begin-end iterator pair.
} else if constexpr ( //
requires { _Container(std::declval<_InputIter>(), std::declval<_InputIter>(), std::declval<_Args>()...); }) {
using _Result =
decltype(_Container(std::declval<_InputIter>(), std::declval<_InputIter>(), std::declval<_Args>()...));
return type_identity<_Result>{};
} else {
static_assert(__always_false<_Range>,
"ranges::to: unable to deduce the container type from the template template argument.");
}
}
using type = typename decltype(__deduce_func())::type;
};
// `ranges::to` specialization -- `_Container` is a template template parameter requiring deduction to figure out the
// container element type.
template <template <class...> class _Container, input_range _Range, class... _Args>
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto to(_Range&& __range, _Args&&... __args) {
using _DeduceExpr = typename _Deducer<_Container, _Range, _Args...>::type;
return ranges::to<_DeduceExpr>(std::forward<_Range>(__range), std::forward<_Args>(__args)...);
}
// Range adaptor closure object 1 -- wrapping the `ranges::to` version where `_Container` is a simple type template
// parameter.
template <class _Container, class... _Args>
requires(!view<_Container>)
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto to(_Args&&... __args) {
// Mandates: C is a cv-unqualified class type.
static_assert(!is_const_v<_Container>, "The target container cannot be const-qualified, please remove the const");
static_assert(
!is_volatile_v<_Container>, "The target container cannot be volatile-qualified, please remove the volatile");
auto __to_func = []<input_range _Range, class... _Tail>(_Range && __range, _Tail && ... __tail)
requires requires { //
/**/ ranges::to<_Container>(std::forward<_Range>(__range), std::forward<_Tail>(__tail)...);
}
{
return ranges::to<_Container>(std::forward<_Range>(__range), std::forward<_Tail>(__tail)...);
};
return __range_adaptor_closure_t(std::__bind_back(__to_func, std::forward<_Args>(__args)...));
}
// Range adaptor closure object 2 -- wrapping the `ranges::to` version where `_Container` is a template template
// parameter.
template <template <class...> class _Container, class... _Args>
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto to(_Args&&... __args) {
// clang-format off
auto __to_func = []<input_range _Range, class... _Tail,
class _DeducedExpr = typename _Deducer<_Container, _Range, _Tail...>::type>
(_Range&& __range, _Tail&& ... __tail)
requires requires { //
/**/ ranges::to<_DeducedExpr>(std::forward<_Range>(__range), std::forward<_Tail>(__tail)...);
}
{
return ranges::to<_DeducedExpr>(std::forward<_Range>(__range), std::forward<_Tail>(__tail)...);
};
// clang-format on
return __range_adaptor_closure_t(std::__bind_back(__to_func, std::forward<_Args>(__args)...));
}
} // namespace ranges
#endif // _LIBCPP_STD_VER >= 23
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___RANGES_TO_H

View File

@ -1718,6 +1718,7 @@ module std_private_ranges_subrange_fwd [system] {
}
module std_private_ranges_take_view [system] { header "__ranges/take_view.h" }
module std_private_ranges_take_while_view [system] { header "__ranges/take_while_view.h" }
module std_private_ranges_to [system] { header "__ranges/to.h" }
module std_private_ranges_transform_view [system] {
header "__ranges/transform_view.h"
export std_private_functional_bind_back

View File

@ -138,6 +138,16 @@ namespace std::ranges {
inline constexpr auto values = elements<1>;
}
// [range.utility.conv], range conversions
template<class C, input_range R, class... Args> requires (!view<C>)
constexpr C to(R&& r, Args&&... args); // Since C++23
template<template<class...> class C, input_range R, class... Args>
constexpr auto to(R&& r, Args&&... args); // Since C++23
template<class C, class... Args> requires (!view<C>)
constexpr auto to(Args&&... args); // Since C++23
template<template<class...> class C, class... Args>
constexpr auto to(Args&&... args); // Since C++23
// [range.empty], empty view
template<class T>
requires is_object_v<T>
@ -391,6 +401,7 @@ namespace std {
#include <__ranges/subrange.h>
#include <__ranges/take_view.h>
#include <__ranges/take_while_view.h>
#include <__ranges/to.h>
#include <__ranges/transform_view.h>
#include <__ranges/view_interface.h>
#include <__ranges/views.h>

View File

@ -162,7 +162,7 @@ __cpp_lib_ranges_repeat 202207L <ranges>
__cpp_lib_ranges_slide 202202L <ranges>
__cpp_lib_ranges_starts_ends_with 202106L <algorithm>
__cpp_lib_ranges_to_container 202202L <deque> <forward_list> <list>
<map> <priority_queue> <queue>
<map> <queue> <ranges>
<set> <stack> <string>
<unordered_map> <unordered_set> <vector>
__cpp_lib_ranges_zip 202110L <ranges> <tuple> <utility>
@ -440,7 +440,7 @@ __cpp_lib_within_lifetime 202306L <type_traits>
# define __cpp_lib_ranges_repeat 202207L
// # define __cpp_lib_ranges_slide 202202L
// # define __cpp_lib_ranges_starts_ends_with 202106L
// # define __cpp_lib_ranges_to_container 202202L
# define __cpp_lib_ranges_to_container 202202L
// # define __cpp_lib_ranges_zip 202110L
// # define __cpp_lib_reference_from_temporary 202202L
// # define __cpp_lib_spanstream 202106L

View File

@ -95,7 +95,7 @@ export namespace std {
using std::ranges::borrowed_subrange_t;
// [range.utility.conv], range conversions
// using std::ranges::to;
using std::ranges::to;
// [range.empty], empty view
using std::ranges::empty_view;

View File

@ -0,0 +1,94 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// Test that the "mandates" requirements on the given container are checked correctly using `static_assert`.
#include <ranges>
#include <vector>
template <bool HasDefaultCtr = true, bool HasSingleArgCtr = true,
bool HasInsert = true, bool HasInsertWithRightSignature = true,
bool HasPushBack = true, bool HasPushBackWithRightSignature = true>
struct Container {
using value_type = int;
int* begin() const { return nullptr; }
int* end() const { return nullptr; }
Container()
requires HasDefaultCtr = default;
Container(int)
requires HasSingleArgCtr {
}
int* insert(int*, int)
requires (HasInsert && HasInsertWithRightSignature) {
return nullptr;
}
int* insert()
requires (HasInsert && !HasInsertWithRightSignature) {
return nullptr;
}
void push_back(int)
requires (HasPushBack && HasPushBackWithRightSignature) {
}
void push_back()
requires (HasPushBack && !HasPushBackWithRightSignature) {
}
};
void test() {
using R = std::vector<int>;
R in = {1, 2, 3};
// Case 4 -- default-construct (or construct from the extra arguments) and insert.
{ // All constraints satisfied.
using C = Container<>;
(void)std::ranges::to<C>(in);
(void)std::ranges::to<C>(in, 1);
(void)std::ranges::to<C>(in, 1.0);
}
{ // No default constructor.
using C = Container</*HasDefaultCtr=*/false>;
(void)std::ranges::to<C>(in); //expected-error-re@*:* {{{{(static_assert|static assertion)}} failed{{.*}}ranges::to: unable to convert to the given container type}}
}
{ // No single-argument constructor.
using C = Container</*HasDefaultCtr=*/true, /*HasSingleArgCtr=*/false>;
(void)std::ranges::to<C>(in, 1); //expected-error-re@*:* {{{{(static_assert|static assertion)}} failed{{.*}}ranges::to: unable to convert to the given container type}}
}
{ // No `insert` and no `push_back`.
using C = Container</*HasDefaultCtr=*/true, /*HasSingleArgCtr=*/true,
/*HasInsert=*/false, /*HasInsertWithRightSignature=*/false,
/*HasPushBack=*/false, /*HasPushBackWithRightSignature=*/false>;
(void)std::ranges::to<C>(in); //expected-error-re@*:* {{{{(static_assert|static assertion)}} failed{{.*}}ranges::to: unable to convert to the given container type}}
}
{ // No `push_back`, `insert` has a wrong signature.
using C = Container</*HasDefaultCtr=*/true, /*HasSingleArgCtr=*/true,
/*HasInsert=*/true, /*HasInsertWithRightSignature=*/false,
/*HasPushBack=*/false, /*HasPushBackWithRightSignature=*/false>;
(void)std::ranges::to<C>(in); //expected-error-re@*:* {{{{(static_assert|static assertion)}} failed{{.*}}ranges::to: unable to convert to the given container type}}
}
{ // No `insert`, `push_back` has a wrong signature.
using C = Container</*HasDefaultCtr=*/true, /*HasSingleArgCtr=*/true,
/*HasInsert=*/false, /*HasInsertWithRightSignature=*/false,
/*HasPushBack=*/true, /*HasPushBackWithRightSignature=*/false>;
(void)std::ranges::to<C>(in); //expected-error-re@*:* {{{{(static_assert|static assertion)}} failed{{.*}}ranges::to: unable to convert to the given container type}}
}
}

View File

@ -0,0 +1,29 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// Test the libc++ extension that `std::ranges::to` is marked as [[nodiscard]].
#include <ranges>
#include <vector>
void test() {
using R = std::vector<int>;
R in = {1, 2, 3};
std::allocator<int> alloc;
std::ranges::to<R>(in); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::ranges::to<R>(in, alloc); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::ranges::to<std::vector>(in); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::ranges::to<std::vector>(in, alloc); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
in | std::ranges::to<R>(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
in | std::ranges::to<R>(alloc); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
in | std::ranges::to<std::vector>(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
in | std::ranges::to<std::vector>(alloc); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
}

View File

@ -0,0 +1,25 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// ADDITIONAL_COMPILE_FLAGS: -Wno-deprecated-volatile
// Test that the "mandates" requirements on the given container are checked using `static_assert`.
#include <ranges>
#include <vector>
void test() {
using R = std::vector<int>;
R in = {1, 2, 3};
(void)std::ranges::to<const R>(in); //expected-error-re@*:* {{{{(static_assert|static assertion)}} failed{{.*}}The target container cannot be const-qualified, please remove the const}}
(void)(in | std::ranges::to<const R>()); //expected-error-re@*:* {{{{(static_assert|static assertion)}} failed{{.*}}The target container cannot be const-qualified, please remove the const}}
(void)std::ranges::to<volatile R>(in); //expected-error-re@*:* {{{{(static_assert|static assertion)}} failed{{.*}}The target container cannot be volatile-qualified, please remove the volatile}}
(void)(in | std::ranges::to<volatile R>()); //expected-error-re@*:* {{{{(static_assert|static assertion)}} failed{{.*}}The target container cannot be volatile-qualified, please remove the volatile}}
}

View File

@ -135,17 +135,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++23"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
#elif TEST_STD_VER > 23
@ -171,17 +165,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
#endif // TEST_STD_VER > 23

View File

@ -192,17 +192,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++23"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
#elif TEST_STD_VER > 23
@ -242,17 +236,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
#endif // TEST_STD_VER > 23

View File

@ -192,17 +192,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++23"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
#elif TEST_STD_VER > 23
@ -242,17 +236,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
#endif // TEST_STD_VER > 23

View File

@ -279,17 +279,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++23"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
#elif TEST_STD_VER > 23
@ -362,17 +356,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
#endif // TEST_STD_VER > 23

View File

@ -72,17 +72,11 @@
# error "__cpp_lib_adaptor_iterator_pair_constructor should have the value 202106L in c++23"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
#elif TEST_STD_VER > 23
@ -94,17 +88,11 @@
# error "__cpp_lib_adaptor_iterator_pair_constructor should have the value 202106L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
#endif // TEST_STD_VER > 23

View File

@ -15,15 +15,16 @@
// Test the feature test macros defined by <ranges>
/* Constant Value
__cpp_lib_ranges 202207L [C++20]
__cpp_lib_ranges_as_rvalue 202207L [C++23]
__cpp_lib_ranges_chunk 202202L [C++23]
__cpp_lib_ranges_chunk_by 202202L [C++23]
__cpp_lib_ranges_join_with 202202L [C++23]
__cpp_lib_ranges_repeat 202207L [C++23]
__cpp_lib_ranges_slide 202202L [C++23]
__cpp_lib_ranges_zip 202110L [C++23]
/* Constant Value
__cpp_lib_ranges 202207L [C++20]
__cpp_lib_ranges_as_rvalue 202207L [C++23]
__cpp_lib_ranges_chunk 202202L [C++23]
__cpp_lib_ranges_chunk_by 202202L [C++23]
__cpp_lib_ranges_join_with 202202L [C++23]
__cpp_lib_ranges_repeat 202207L [C++23]
__cpp_lib_ranges_slide 202202L [C++23]
__cpp_lib_ranges_to_container 202202L [C++23]
__cpp_lib_ranges_zip 202110L [C++23]
*/
#include <ranges>
@ -59,6 +60,10 @@
# error "__cpp_lib_ranges_slide should not be defined before c++23"
# endif
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined before c++23"
# endif
# ifdef __cpp_lib_ranges_zip
# error "__cpp_lib_ranges_zip should not be defined before c++23"
# endif
@ -93,6 +98,10 @@
# error "__cpp_lib_ranges_slide should not be defined before c++23"
# endif
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined before c++23"
# endif
# ifdef __cpp_lib_ranges_zip
# error "__cpp_lib_ranges_zip should not be defined before c++23"
# endif
@ -127,6 +136,10 @@
# error "__cpp_lib_ranges_slide should not be defined before c++23"
# endif
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined before c++23"
# endif
# ifdef __cpp_lib_ranges_zip
# error "__cpp_lib_ranges_zip should not be defined before c++23"
# endif
@ -164,6 +177,10 @@
# error "__cpp_lib_ranges_slide should not be defined before c++23"
# endif
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined before c++23"
# endif
# ifdef __cpp_lib_ranges_zip
# error "__cpp_lib_ranges_zip should not be defined before c++23"
# endif
@ -243,6 +260,13 @@
# endif
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_zip
# error "__cpp_lib_ranges_zip should be defined in c++23"
@ -331,6 +355,13 @@
# endif
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_zip
# error "__cpp_lib_ranges_zip should be defined in c++26"

View File

@ -249,17 +249,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++23"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
#elif TEST_STD_VER > 23
@ -325,17 +319,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
#endif // TEST_STD_VER > 23

View File

@ -72,17 +72,11 @@
# error "__cpp_lib_adaptor_iterator_pair_constructor should have the value 202106L in c++23"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
#elif TEST_STD_VER > 23
@ -94,17 +88,11 @@
# error "__cpp_lib_adaptor_iterator_pair_constructor should have the value 202106L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
#endif // TEST_STD_VER > 23

View File

@ -322,17 +322,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++23"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
# ifndef __cpp_lib_starts_ends_with
@ -426,17 +420,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
# ifndef __cpp_lib_starts_ends_with

View File

@ -266,17 +266,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++23"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
# ifndef __cpp_lib_unordered_map_try_emplace
@ -349,17 +343,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
# ifndef __cpp_lib_unordered_map_try_emplace

View File

@ -243,17 +243,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++23"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
#elif TEST_STD_VER > 23
@ -319,17 +313,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
#endif // TEST_STD_VER > 23

View File

@ -192,17 +192,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++23"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
#elif TEST_STD_VER > 23
@ -242,17 +236,11 @@
# error "__cpp_lib_nonmember_container_access should have the value 201411L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
#endif // TEST_STD_VER > 23

View File

@ -5042,17 +5042,11 @@
# endif
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++23"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++23"
# endif
# if !defined(_LIBCPP_VERSION)
@ -6601,17 +6595,11 @@
# endif
# endif
# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_to_container
# error "__cpp_lib_ranges_to_container should be defined in c++26"
# endif
# if __cpp_lib_ranges_to_container != 202202L
# error "__cpp_lib_ranges_to_container should have the value 202202L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)

View File

@ -0,0 +1,144 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef RANGES_RANGE_UTILITY_RANGE_UTILITY_CONV_CONTAINER_H
#define RANGES_RANGE_UTILITY_RANGE_UTILITY_CONV_CONTAINER_H
#include <algorithm>
#include <concepts>
#include <cstddef>
enum class CtrChoice { Invalid, DefaultCtrAndInsert, BeginEndPair, FromRangeT, DirectCtr };
enum class InserterChoice { Invalid, Insert, PushBack };
// Allows checking that `ranges::to` correctly follows the order of priority of different constructors -- e.g., if
// 3 constructors are available, the `from_range_t` constructor is chosen in favor of the constructor taking two
// iterators, etc.
template <class ElementType, CtrChoice Rank, InserterChoice Inserter = InserterChoice::Insert, bool CanReserve = false>
struct Container {
CtrChoice ctr_choice = CtrChoice::Invalid;
InserterChoice inserter_choice = InserterChoice::Invalid;
bool called_reserve = false;
int extra_arg1 = 0;
char extra_arg2 = 0;
using value_type = ElementType;
static constexpr int Capacity = 8;
int size_ = 0;
ElementType buffer_[Capacity] = {};
// Case 1 -- construct directly from the range.
constexpr explicit Container(std::ranges::input_range auto&& in)
requires(Rank >= CtrChoice::DirectCtr)
: ctr_choice(CtrChoice::DirectCtr), size_(std::ranges::size(in)) {
std::ranges::copy(in, begin());
}
// Check that `ranges::to` can also pass extra parameters.
constexpr explicit Container(std::ranges::input_range auto&& in, int arg1, char arg2)
requires(Rank >= CtrChoice::DirectCtr)
: Container(in) {
extra_arg1 = arg1;
extra_arg2 = arg2;
}
// Case 2 -- use `from_range_t` constructor.
constexpr Container(std::from_range_t, std::ranges::input_range auto&& in)
requires(Rank >= CtrChoice::FromRangeT)
: ctr_choice(CtrChoice::FromRangeT), size_(std::ranges::size(in)) {
std::ranges::copy(in, begin());
}
constexpr Container(std::from_range_t, std::ranges::input_range auto&& in, int arg1, char arg2)
requires(Rank >= CtrChoice::FromRangeT)
: Container(std::from_range, in) {
extra_arg1 = arg1;
extra_arg2 = arg2;
}
// Case 3 -- use begin-end pair.
template <class Iter>
constexpr Container(Iter b, Iter e)
requires(Rank >= CtrChoice::BeginEndPair)
: ctr_choice(CtrChoice::BeginEndPair), size_(e - b) {
std::ranges::copy(b, e, begin());
}
template <class Iter>
constexpr Container(Iter b, Iter e, int arg1, char arg2)
requires(Rank >= CtrChoice::BeginEndPair)
: Container(b, e) {
extra_arg1 = arg1;
extra_arg2 = arg2;
}
// Case 4 -- default-construct and insert, reserving the size if possible.
constexpr Container()
requires(Rank >= CtrChoice::DefaultCtrAndInsert)
: ctr_choice(CtrChoice::DefaultCtrAndInsert) {}
constexpr Container(int arg1, char arg2)
requires(Rank >= CtrChoice::DefaultCtrAndInsert)
: ctr_choice(CtrChoice::DefaultCtrAndInsert), extra_arg1(arg1), extra_arg2(arg2) {}
constexpr ElementType* begin() { return buffer_; }
constexpr ElementType* end() { return buffer_ + size_; }
constexpr std::size_t size() const { return size_; }
template <class T>
constexpr void push_back(T val)
requires(Inserter >= InserterChoice::PushBack)
{
inserter_choice = InserterChoice::PushBack;
buffer_[size_] = val;
++size_;
}
template <class T>
constexpr ElementType* insert(ElementType* where, T val)
requires(Inserter >= InserterChoice::Insert)
{
assert(size() + 1 <= Capacity);
inserter_choice = InserterChoice::Insert;
std::shift_right(where, end(), 1);
*where = val;
++size_;
return where;
}
constexpr void reserve(size_t)
requires CanReserve
{
called_reserve = true;
}
constexpr std::size_t capacity() const
requires CanReserve
{
return Capacity;
}
constexpr std::size_t max_size() const
requires CanReserve
{
return Capacity;
}
friend constexpr bool operator==(const Container&, const Container&) = default;
};
#endif // RANGES_RANGE_UTILITY_RANGE_UTILITY_CONV_CONTAINER_H

View File

@ -0,0 +1,574 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// template<class C, input_range R, class... Args> requires (!view<C>)
// constexpr C to(R&& r, Args&&... args); // Since C++23
#include <ranges>
#include <algorithm>
#include <array>
#include <cassert>
#include <vector>
#include "container.h"
#include "test_iterators.h"
#include "test_range.h"
template <class Container, class Range, class... Args>
concept HasTo = requires (Range&& range, Args ...args) {
std::ranges::to<Container>(std::forward<Range>(range), std::forward<Args>(args)...);
};
struct InputRange {
int x = 0;
constexpr cpp20_input_iterator<int*> begin() {
return cpp20_input_iterator<int*>(&x);
}
constexpr sentinel_wrapper<cpp20_input_iterator<int*>> end() {
return sentinel_wrapper<cpp20_input_iterator<int*>>(begin());
}
};
static_assert(std::ranges::input_range<InputRange>);
struct common_cpp20_input_iterator {
using value_type = int;
using difference_type = long long;
using iterator_concept = std::input_iterator_tag;
// Deliberately not defining `iterator_category` to make sure this class satisfies the `input_iterator` concept but
// would fail `derived_from<iterator_category, input_iterator_tag>`.
int x = 0;
// Copyable so that it can be used as a sentinel against itself.
constexpr decltype(auto) operator*() const { return x; }
constexpr common_cpp20_input_iterator& operator++() { return *this; }
constexpr void operator++(int) {}
constexpr friend bool operator==(common_cpp20_input_iterator, common_cpp20_input_iterator) { return true; }
};
static_assert(std::input_iterator<common_cpp20_input_iterator>);
static_assert(std::sentinel_for<common_cpp20_input_iterator, common_cpp20_input_iterator>);
template <class T>
concept HasIteratorCategory = requires {
typename std::iterator_traits<T>::iterator_category;
};
static_assert(!HasIteratorCategory<common_cpp20_input_iterator>);
struct CommonInputRange {
int x = 0;
constexpr common_cpp20_input_iterator begin() { return {}; }
constexpr common_cpp20_input_iterator end() { return begin(); }
};
static_assert(std::ranges::input_range<CommonInputRange>);
static_assert(std::ranges::common_range<CommonInputRange>);
struct CommonRange {
int x = 0;
constexpr forward_iterator<int*> begin() {
return forward_iterator<int*>(&x);
}
constexpr forward_iterator<int*> end() {
return begin();
}
};
static_assert(std::ranges::input_range<CommonRange>);
static_assert(std::ranges::common_range<CommonRange>);
struct NonCommonRange {
int x = 0;
constexpr forward_iterator<int*> begin() {
return forward_iterator<int*>(&x);
}
constexpr sentinel_wrapper<forward_iterator<int*>> end() {
return sentinel_wrapper<forward_iterator<int*>>(begin());
}
};
static_assert(std::ranges::input_range<NonCommonRange>);
static_assert(!std::ranges::common_range<NonCommonRange>);
static_assert(std::derived_from<
typename std::iterator_traits<std::ranges::iterator_t<NonCommonRange>>::iterator_category,
std::input_iterator_tag>);
using ContainerT = int;
static_assert(!std::ranges::view<ContainerT>);
static_assert(HasTo<ContainerT, InputRange>);
static_assert(!HasTo<test_view<forward_iterator>, InputRange>);
// Note: it's not possible to check the `input_range` constraint because if it's not satisfied, the pipe adaptor
// overload hijacks the call (it takes unconstrained variadic arguments).
// Check the exact constraints for each one of the cases inside `ranges::to`.
struct Empty {};
struct Fallback {
using value_type = int;
CtrChoice ctr_choice = CtrChoice::Invalid;
int x = 0;
constexpr Fallback() : ctr_choice(CtrChoice::DefaultCtrAndInsert) {}
constexpr Fallback(Empty) : ctr_choice(CtrChoice::DefaultCtrAndInsert) {}
constexpr void push_back(value_type) {}
constexpr value_type* begin() { return &x; }
constexpr value_type* end() { return &x; }
};
struct CtrDirectOrFallback : Fallback {
using Fallback::Fallback;
constexpr CtrDirectOrFallback(InputRange&&, int = 0) { ctr_choice = CtrChoice::DirectCtr; }
};
struct CtrFromRangeTOrFallback : Fallback {
using Fallback::Fallback;
constexpr CtrFromRangeTOrFallback(std::from_range_t, InputRange&&, int = 0) { ctr_choice = CtrChoice::FromRangeT; }
};
struct CtrBeginEndPairOrFallback : Fallback {
using Fallback::Fallback;
template <class Iter>
constexpr CtrBeginEndPairOrFallback(Iter, Iter, int = 0) { ctr_choice = CtrChoice::BeginEndPair; }
};
template <bool HasSize>
struct MaybeSizedRange {
int x = 0;
constexpr forward_iterator<int*> begin() { return forward_iterator<int*>(&x); }
constexpr forward_iterator<int*> end() { return begin(); }
constexpr std::size_t size() const
requires HasSize {
return 0;
}
};
static_assert(std::ranges::sized_range<MaybeSizedRange<true>>);
static_assert(!std::ranges::sized_range<MaybeSizedRange<false>>);
template <bool HasCapacity = true, bool CapacityReturnsSizeT = true,
bool HasMaxSize = true, bool MaxSizeReturnsSizeT = true>
struct Reservable : Fallback {
bool reserve_called = false;
using Fallback::Fallback;
constexpr std::size_t capacity() const
requires (HasCapacity && CapacityReturnsSizeT) {
return 0;
}
constexpr int capacity() const
requires (HasCapacity && !CapacityReturnsSizeT) {
return 0;
}
constexpr std::size_t max_size() const
requires (HasMaxSize && MaxSizeReturnsSizeT) {
return 0;
}
constexpr int max_size() const
requires (HasMaxSize && !MaxSizeReturnsSizeT) {
return 0;
}
constexpr void reserve(std::size_t) {
reserve_called = true;
}
};
static_assert(std::ranges::__reservable_container<Reservable<>>);
constexpr void test_constraints() {
{ // Case 1 -- construct directly from the range.
{ // (range)
auto result = std::ranges::to<CtrDirectOrFallback>(InputRange());
assert(result.ctr_choice == CtrChoice::DirectCtr);
}
{ // (range, arg)
auto result = std::ranges::to<CtrDirectOrFallback>(InputRange(), 1);
assert(result.ctr_choice == CtrChoice::DirectCtr);
}
{ // (range, convertible-to-arg)
auto result = std::ranges::to<CtrDirectOrFallback>(InputRange(), 1.0);
assert(result.ctr_choice == CtrChoice::DirectCtr);
}
{ // (range, BAD_arg)
auto result = std::ranges::to<CtrDirectOrFallback>(InputRange(), Empty());
assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
}
}
{ // Case 2 -- construct using the `from_range_t` tagged constructor.
{ // (range)
auto result = std::ranges::to<CtrFromRangeTOrFallback>(InputRange());
assert(result.ctr_choice == CtrChoice::FromRangeT);
}
{ // (range, arg)
auto result = std::ranges::to<CtrFromRangeTOrFallback>(InputRange(), 1);
assert(result.ctr_choice == CtrChoice::FromRangeT);
}
{ // (range, convertible-to-arg)
auto result = std::ranges::to<CtrFromRangeTOrFallback>(InputRange(), 1.0);
assert(result.ctr_choice == CtrChoice::FromRangeT);
}
{ // (range, BAD_arg)
auto result = std::ranges::to<CtrFromRangeTOrFallback>(InputRange(), Empty());
assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
}
}
{ // Case 3 -- construct from a begin-end iterator pair.
{ // (range)
auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonRange());
assert(result.ctr_choice == CtrChoice::BeginEndPair);
}
{ // (range, arg)
auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonRange(), 1);
assert(result.ctr_choice == CtrChoice::BeginEndPair);
}
{ // (range, convertible-to-arg)
auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonRange(), 1.0);
assert(result.ctr_choice == CtrChoice::BeginEndPair);
}
{ // (BAD_range) -- not a common range.
auto result = std::ranges::to<CtrBeginEndPairOrFallback>(NonCommonRange());
assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
}
{ // (BAD_range) -- iterator type not derived from `input_iterator_tag`.
auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonInputRange());
assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
}
{ // (range, BAD_arg)
auto result = std::ranges::to<CtrBeginEndPairOrFallback>(CommonRange(), Empty());
assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
}
}
{ // Case 4 -- default-construct (or construct from the extra arguments) and insert, reserving the size if possible.
// Note: it's not possible to check the constraints on the default constructor using this approach because there is
// nothing to fall back to -- the call will result in a hard error.
// However, it's possible to check the constraints on reserving the capacity.
{ // All constraints satisfied.
using C = Reservable<>;
auto result = std::ranges::to<C>(MaybeSizedRange<true>());
assert(result.reserve_called);
}
{ // !sized_range
using C = Reservable<>;
auto result = std::ranges::to<C>(MaybeSizedRange<false>());
assert(!result.reserve_called);
}
{ // Missing `capacity`.
using C = Reservable</*HasCapacity=*/false>;
auto result = std::ranges::to<C>(MaybeSizedRange<true>());
assert(!result.reserve_called);
}
{ // `capacity` doesn't return `size_type`.
using C = Reservable</*HasCapacity=*/true, /*CapacityReturnsSizeT=*/false>;
auto result = std::ranges::to<C>(MaybeSizedRange<true>());
assert(!result.reserve_called);
}
{ // Missing `max_size`.
using C = Reservable</*HasCapacity=*/true, /*CapacityReturnsSizeT=*/true, /*HasMaxSize=*/false>;
auto result = std::ranges::to<C>(MaybeSizedRange<true>());
assert(!result.reserve_called);
}
{ // `max_size` doesn't return `size_type`.
using C = Reservable<
/*HasCapacity=*/true, /*CapacityReturnsSizeT=*/true, /*HasMaxSize=*/true, /*MaxSizeReturnsSizeT=*/false>;
auto result = std::ranges::to<C>(MaybeSizedRange<true>());
assert(!result.reserve_called);
}
}
}
constexpr void test_ctr_choice_order() {
std::array in = {1, 2, 3, 4, 5};
int arg1 = 42;
char arg2 = 'a';
{ // Case 1 -- construct directly from the given range.
{
using C = Container<int, CtrChoice::DirectCtr>;
std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
assert(result.ctr_choice == CtrChoice::DirectCtr);
assert(std::ranges::equal(result, in));
assert((in | std::ranges::to<C>()) == result);
auto closure = std::ranges::to<C>();
assert((in | closure) == result);
}
{ // Extra arguments.
using C = Container<int, CtrChoice::DirectCtr>;
std::same_as<C> decltype(auto) result = std::ranges::to<C>(in, arg1, arg2);
assert(result.ctr_choice == CtrChoice::DirectCtr);
assert(std::ranges::equal(result, in));
assert(result.extra_arg1 == arg1);
assert(result.extra_arg2 == arg2);
assert((in | std::ranges::to<C>(arg1, arg2)) == result);
auto closure = std::ranges::to<C>(arg1, arg2);
assert((in | closure) == result);
}
}
{ // Case 2 -- construct using the `from_range_t` tag.
{
using C = Container<int, CtrChoice::FromRangeT>;
std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
assert(result.ctr_choice == CtrChoice::FromRangeT);
assert(std::ranges::equal(result, in));
assert((in | std::ranges::to<C>()) == result);
auto closure = std::ranges::to<C>();
assert((in | closure) == result);
}
{ // Extra arguments.
using C = Container<int, CtrChoice::FromRangeT>;
std::same_as<C> decltype(auto) result = std::ranges::to<C>(in, arg1, arg2);
assert(result.ctr_choice == CtrChoice::FromRangeT);
assert(std::ranges::equal(result, in));
assert(result.extra_arg1 == arg1);
assert(result.extra_arg2 == arg2);
assert((in | std::ranges::to<C>(arg1, arg2)) == result);
auto closure = std::ranges::to<C>(arg1, arg2);
assert((in | closure) == result);
}
}
{ // Case 3 -- construct from a begin-end pair.
{
using C = Container<int, CtrChoice::BeginEndPair>;
std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
assert(result.ctr_choice == CtrChoice::BeginEndPair);
assert(std::ranges::equal(result, in));
assert((in | std::ranges::to<C>()) == result);
auto closure = std::ranges::to<C>();
assert((in | closure) == result);
}
{ // Extra arguments.
using C = Container<int, CtrChoice::BeginEndPair>;
std::same_as<C> decltype(auto) result = std::ranges::to<C>(in, arg1, arg2);
assert(result.ctr_choice == CtrChoice::BeginEndPair);
assert(std::ranges::equal(result, in));
assert(result.extra_arg1 == arg1);
assert(result.extra_arg2 == arg2);
assert((in | std::ranges::to<C>(arg1, arg2)) == result);
auto closure = std::ranges::to<C>(arg1, arg2);
assert((in | closure) == result);
}
}
{ // Case 4 -- default-construct then insert elements.
{
using C = Container<int, CtrChoice::DefaultCtrAndInsert, InserterChoice::Insert, /*CanReserve=*/false>;
std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
assert(result.inserter_choice == InserterChoice::Insert);
assert(std::ranges::equal(result, in));
assert(!result.called_reserve);
assert((in | std::ranges::to<C>()) == result);
auto closure = std::ranges::to<C>();
assert((in | closure) == result);
}
{
using C = Container<int, CtrChoice::DefaultCtrAndInsert, InserterChoice::Insert, /*CanReserve=*/true>;
std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
assert(result.inserter_choice == InserterChoice::Insert);
assert(std::ranges::equal(result, in));
assert(result.called_reserve);
assert((in | std::ranges::to<C>()) == result);
auto closure = std::ranges::to<C>();
assert((in | closure) == result);
}
{
using C = Container<int, CtrChoice::DefaultCtrAndInsert, InserterChoice::PushBack, /*CanReserve=*/false>;
std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
assert(result.inserter_choice == InserterChoice::PushBack);
assert(std::ranges::equal(result, in));
assert(!result.called_reserve);
assert((in | std::ranges::to<C>()) == result);
auto closure = std::ranges::to<C>();
assert((in | closure) == result);
}
{
using C = Container<int, CtrChoice::DefaultCtrAndInsert, InserterChoice::PushBack, /*CanReserve=*/true>;
std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
assert(result.inserter_choice == InserterChoice::PushBack);
assert(std::ranges::equal(result, in));
assert(result.called_reserve);
assert((in | std::ranges::to<C>()) == result);
auto closure = std::ranges::to<C>();
assert((in | closure) == result);
}
{ // Extra arguments.
using C = Container<int, CtrChoice::DefaultCtrAndInsert, InserterChoice::Insert, /*CanReserve=*/false>;
std::same_as<C> decltype(auto) result = std::ranges::to<C>(in, arg1, arg2);
assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
assert(result.inserter_choice == InserterChoice::Insert);
assert(std::ranges::equal(result, in));
assert(!result.called_reserve);
assert(result.extra_arg1 == arg1);
assert(result.extra_arg2 == arg2);
assert((in | std::ranges::to<C>(arg1, arg2)) == result);
auto closure = std::ranges::to<C>(arg1, arg2);
assert((in | closure) == result);
}
}
}
template <CtrChoice Rank>
struct NotARange {
using value_type = int;
constexpr NotARange(std::ranges::input_range auto&&)
requires (Rank >= CtrChoice::DirectCtr)
{}
constexpr NotARange(std::from_range_t, std::ranges::input_range auto&&)
requires (Rank >= CtrChoice::FromRangeT)
{}
template <class Iter>
constexpr NotARange(Iter, Iter)
requires (Rank >= CtrChoice::BeginEndPair)
{}
constexpr NotARange()
requires (Rank >= CtrChoice::DefaultCtrAndInsert)
= default;
constexpr void push_back(int) {}
};
static_assert(!std::ranges::range<NotARange<CtrChoice::DirectCtr>>);
constexpr void test_lwg_3785() {
// Test LWG 3785 ("`ranges::to` is over-constrained on the destination type being a range") -- make sure it's possible
// to convert the given input range to a non-range type.
std::array in = {1, 2, 3, 4, 5};
{
using C = NotARange<CtrChoice::DirectCtr>;
[[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
}
{
using C = NotARange<CtrChoice::FromRangeT>;
[[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
}
{
using C = NotARange<CtrChoice::BeginEndPair>;
[[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
}
{
using C = NotARange<CtrChoice::DefaultCtrAndInsert>;
[[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
}
}
constexpr void test_recursive() {
using C1 = Container<int, CtrChoice::DirectCtr>;
using C2 = Container<C1, CtrChoice::FromRangeT>;
using C3 = Container<C2, CtrChoice::BeginEndPair>;
using C4 = Container<C3, CtrChoice::DefaultCtrAndInsert, InserterChoice::PushBack>;
using A1 = std::array<int, 4>;
using A2 = std::array<A1, 3>;
using A3 = std::array<A2, 2>;
using A4 = std::array<A3, 2>;
A4 in = {};
{ // Fill the nested array with incremental values.
int x = 0;
for (auto& a3 : in) {
for (auto& a2 : a3) {
for (auto& a1 : a2) {
for (int& el : a1) {
el = x++;
}
}
}
}
}
std::same_as<C4> decltype(auto) result = std::ranges::to<C4>(in);
assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
int expected_value = 0;
for (auto& c3 : result) {
assert(c3.ctr_choice == CtrChoice::BeginEndPair);
for (auto& c2 : c3) {
assert(c2.ctr_choice == CtrChoice::FromRangeT);
for (auto& c1 : c2) {
assert(c1.ctr_choice == CtrChoice::DirectCtr);
for (int el : c1) {
assert(el == expected_value);
++expected_value;
}
}
}
}
assert((in | std::ranges::to<C4>()) == result);
}
constexpr bool test() {
test_constraints();
test_ctr_choice_order();
test_lwg_3785();
test_recursive();
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,135 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// There is a bug in older versions of Clang that causes trouble with constraints in classes like
// `ContainerWithDirectCtr`.
// XFAIL: clang-16, apple-clang-15
// template<template<class...> class C, input_range R, class... Args>
// constexpr auto to(R&& r, Args&&... args); // Since C++23
#include <ranges>
#include <algorithm>
#include <array>
#include <cassert>
#include "container.h"
template <class ElementType>
struct ContainerWithDirectCtr : Container<ElementType, CtrChoice::DirectCtr> {
using Container<ElementType, CtrChoice::DirectCtr>::Container;
};
template <std::ranges::input_range Range>
ContainerWithDirectCtr(Range&&) -> ContainerWithDirectCtr<std::ranges::range_value_t<Range>>;
template <std::ranges::input_range Range>
ContainerWithDirectCtr(Range&&, int, char) -> ContainerWithDirectCtr<std::ranges::range_value_t<Range>>;
template <class ElementType>
struct ContainerWithFromRangeT : Container<ElementType, CtrChoice::FromRangeT> {
using Container<ElementType, CtrChoice::FromRangeT>::Container;
};
template <std::ranges::input_range Range>
ContainerWithFromRangeT(std::from_range_t, Range&&) -> ContainerWithFromRangeT<std::ranges::range_value_t<Range>>;
template <std::ranges::input_range Range>
ContainerWithFromRangeT(std::from_range_t, Range&&, int, char) ->
ContainerWithFromRangeT<std::ranges::range_value_t<Range>>;
template <class ElementType>
struct ContainerWithBeginEndPair : Container<ElementType, CtrChoice::BeginEndPair> {
using Container<ElementType, CtrChoice::BeginEndPair>::Container;
};
template <class Iter>
ContainerWithBeginEndPair(Iter, Iter) -> ContainerWithBeginEndPair<std::iter_value_t<Iter>>;
template <class Iter>
ContainerWithBeginEndPair(Iter, Iter, int, char) -> ContainerWithBeginEndPair<std::iter_value_t<Iter>>;
constexpr bool test() {
std::array in = {1, 2, 3, 4, 5};
int arg1 = 42;
char arg2 = 'a';
{ // Case 1 -- can construct directly from the given range.
{
std::same_as<ContainerWithDirectCtr<int>> decltype(auto) result = std::ranges::to<ContainerWithDirectCtr>(in);
assert(result.ctr_choice == CtrChoice::DirectCtr);
assert(std::ranges::equal(result, in));
assert((in | std::ranges::to<ContainerWithDirectCtr>()) == result);
}
{ // Extra arguments.
std::same_as<ContainerWithDirectCtr<int>> decltype(auto) result =
std::ranges::to<ContainerWithDirectCtr>(in, arg1, arg2);
assert(result.ctr_choice == CtrChoice::DirectCtr);
assert(std::ranges::equal(result, in));
assert(result.extra_arg1 == arg1);
assert(result.extra_arg2 == arg2);
assert((in | std::ranges::to<ContainerWithDirectCtr>(arg1, arg2)) == result);
}
}
{ // Case 2 -- can construct from the given range using the `from_range_t` tagged constructor.
{
std::same_as<ContainerWithFromRangeT<int>> decltype(auto) result = std::ranges::to<ContainerWithFromRangeT>(in);
assert(result.ctr_choice == CtrChoice::FromRangeT);
assert(std::ranges::equal(result, in));
assert((in | std::ranges::to<ContainerWithFromRangeT>()) == result);
}
{ // Extra arguments.
std::same_as<ContainerWithFromRangeT<int>> decltype(auto) result =
std::ranges::to<ContainerWithFromRangeT>(in, arg1, arg2);
assert(result.ctr_choice == CtrChoice::FromRangeT);
assert(std::ranges::equal(result, in));
assert(result.extra_arg1 == arg1);
assert(result.extra_arg2 == arg2);
assert((in | std::ranges::to<ContainerWithFromRangeT>(arg1, arg2)) == result);
}
}
{ // Case 3 -- can construct from a begin-end iterator pair.
{
std::same_as<ContainerWithBeginEndPair<int>> decltype(auto) result =
std::ranges::to<ContainerWithBeginEndPair>(in);
assert(result.ctr_choice == CtrChoice::BeginEndPair);
assert(std::ranges::equal(result, in));
assert((in | std::ranges::to<ContainerWithBeginEndPair>()) == result);
}
{ // Extra arguments.
std::same_as<ContainerWithBeginEndPair<int>> decltype(auto) result =
std::ranges::to<ContainerWithBeginEndPair>(in, arg1, arg2);
assert(result.ctr_choice == CtrChoice::BeginEndPair);
assert(std::ranges::equal(result, in));
assert(result.extra_arg1 == arg1);
assert(result.extra_arg2 == arg2);
assert((in | std::ranges::to<ContainerWithBeginEndPair>(arg1, arg2)) == result);
}
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,214 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// Test that `ranges::to` can be used to convert between arbitrary standard containers.
#include <ranges>
#include <algorithm>
#include <cassert>
#include <deque>
#include <forward_list>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "test_iterators.h"
#include "test_range.h"
#include "type_algorithms.h"
#include "unwrap_container_adaptor.h"
std::vector<std::vector<int>> ints = {
{5, 1, 3, 4, 2},
{3},
{}
};
std::vector<std::vector<char>> chars = {
{'a', 'b', 'c'},
{'a'},
{}
};
std::vector<std::vector<std::pair<const int, int>>> pairs = {
{{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 0}},
{{1, 2}},
{}
};
template <class From, class To>
void test_is_equal(std::vector<std::vector<typename From::value_type>> inputs) {
for (const auto& in : inputs) {
From from(in.begin(), in.end());
std::same_as<To> decltype(auto) result = std::ranges::to<To>(from);
assert(std::ranges::equal(in, result));
}
}
template <class From, class To>
void test_is_permutation(std::vector<std::vector<typename From::value_type>> inputs) {
for (const auto& in : inputs) {
From from(in.begin(), in.end());
std::same_as<To> decltype(auto) result = std::ranges::to<To>(in);
assert(std::ranges::is_permutation(in, result));
}
}
template <class From, class To>
void test_is_equal_for_adaptors(std::vector<std::vector<typename From::value_type>> inputs) {
for (const auto& in : inputs) {
From from(in.begin(), in.end());
std::same_as<To> decltype(auto) result = std::ranges::to<To>(in);
UnwrapAdaptor<From> unwrap_from(std::move(from));
UnwrapAdaptor<To> unwrap_to(std::move(result));
assert(std::ranges::is_permutation(unwrap_from.get_container(), unwrap_to.get_container()));
}
}
template <class T>
using sequence_containers = types::type_list<
std::vector<T>,
std::deque<T>,
std::list<T>,
std::forward_list<T>
>;
template <class T>
using associative_sets = types::type_list<
std::set<T>,
std::multiset<T>
>;
template <class K, class V>
using associative_maps = types::type_list<
std::map<K, V>,
std::multimap<K, V>
>;
template <class T>
using unordered_sets = types::type_list<
std::unordered_set<T>,
std::unordered_multiset<T>
>;
template <class K, class V>
using unordered_maps = types::type_list<
std::unordered_map<K, V>,
std::unordered_multimap<K, V>
>;
template <class T>
using container_adaptors = types::type_list<
std::stack<T>,
std::queue<T>,
std::priority_queue<T>
>;
template <class T>
using sequences_and_sets = types::concatenate_t<sequence_containers<T>, associative_sets<T>, unordered_sets<T>>;
template <class K, class V>
using all_containers = types::concatenate_t<
sequence_containers<std::pair<const K, V>>,
associative_sets<std::pair<const K, V>>,
associative_maps<K, V>,
unordered_sets<std::pair<const K, V>>,
unordered_maps<K, V>>;
// This is necessary to be able to use `pair`s with unordered sets.
template <class K, class V>
struct std::hash<std::pair<const K, V>> {
std::size_t operator()(const std::pair<const K, V>& p) const {
std::size_t h1 = std::hash<K>{}(p.first);
std::size_t h2 = std::hash<V>{}(p.second);
return h1 ^ (h2 << 1);
}
};
void test() {
{ // Conversions always preserving equality.
{ // sequences <-> sequences
types::for_each(sequence_containers<int>{}, []<class From>() {
types::for_each(sequence_containers<int>{}, []<class To>() {
test_is_equal<From, To>(ints);
});
});
types::for_each(sequence_containers<int>{}, []<class From>() {
types::for_each(sequence_containers<double>{}, []<class To>() {
test_is_equal<From, To>(ints);
});
});
}
{ // sequences <-> string
types::for_each(sequence_containers<char>{}, []<class Seq>() {
test_is_equal<Seq, std::basic_string<char>>(chars);
test_is_equal<std::basic_string<char>, Seq>(chars);
});
}
}
{ // sequences/sets <-> sequences/sets
types::for_each(sequences_and_sets<int>{}, []<class From>() {
types::for_each(sequences_and_sets<int>{}, []<class To>() {
test_is_permutation<From, To>(ints);
});
});
types::for_each(sequences_and_sets<int>{}, []<class From>() {
types::for_each(sequences_and_sets<double>{}, []<class To>() {
test_is_permutation<From, To>(ints);
});
});
}
{ // sequences/sets/maps <-> sequences/sets/maps. Uses `pair` for non-map containers to allow mutual conversion with
// map types.
types::for_each(all_containers<int, int>{}, []<class From>() {
types::for_each(all_containers<int, int>{}, []<class To>() {
test_is_permutation<From, To>(pairs);
});
});
types::for_each(all_containers<int, int>{}, []<class From>() {
types::for_each(all_containers<long, double>{}, []<class To>() {
test_is_permutation<From, To>(pairs);
});
});
}
{ // adaptors <-> adaptors
types::for_each(container_adaptors<int>{}, []<class From>() {
types::for_each(container_adaptors<int>{}, []<class To>() {
test_is_equal_for_adaptors<From, To>(ints);
});
});
types::for_each(container_adaptors<int>{}, []<class From>() {
types::for_each(container_adaptors<double>{}, []<class To>() {
test_is_equal_for_adaptors<From, To>(ints);
});
});
}
}
int main(int, char**) {
test();
return 0;
}

View File

@ -841,8 +841,8 @@ feature_test_macros = [
"forward_list",
"list",
"map",
"priority_queue",
"queue",
"ranges",
"set",
"stack",
"string",
@ -850,7 +850,6 @@ feature_test_macros = [
"unordered_set",
"vector",
],
"unimplemented": True,
},
{
"name": "__cpp_lib_ranges_zip",