Skip to content

Commit 78bb452

Browse files
<ranges>: Make ranges::to support creating unions (#5794)
Co-authored-by: Stephan T. Lavavej <[email protected]>
1 parent b44cd81 commit 78bb452

File tree

2 files changed

+69
-9
lines changed

2 files changed

+69
-9
lines changed

stl/inc/__msvc_ranges_to.hpp

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,9 +1106,10 @@ namespace ranges {
11061106
_EXPORT_STD template <class _Container, input_range _Rng, class... _Types>
11071107
requires (!view<_Container>)
11081108
_NODISCARD constexpr _Container to(_Rng&& _Range, _Types&&... _Args) {
1109-
static_assert(!is_const_v<_Container>, "C must not be const. ([range.utility.conv.to])");
1110-
static_assert(!is_volatile_v<_Container>, "C must not be volatile. ([range.utility.conv.to])");
1111-
static_assert(is_class_v<_Container>, "C must be a class type. ([range.utility.conv.to])");
1109+
static_assert(!is_const_v<_Container>, "C must not be const. (N5014 [range.utility.conv.to]/1)");
1110+
static_assert(!is_volatile_v<_Container>, "C must not be volatile. (N5014 [range.utility.conv.to]/1)");
1111+
static_assert(is_class_v<_Container> || is_union_v<_Container>,
1112+
"C must be a class type. (N5014 [range.utility.conv.to]/1)");
11121113
if constexpr (_Ref_converts<_Rng, _Container>) {
11131114
if constexpr (constructible_from<_Container, _Rng, _Types...>) {
11141115
return _Container(_STD forward<_Rng>(_Range), _STD forward<_Types>(_Args)...);
@@ -1142,7 +1143,7 @@ namespace ranges {
11421143
} else {
11431144
static_assert(false, "ranges::to requires the result to be constructible from the source range, either "
11441145
"by using a suitable constructor, or by inserting each element of the range into "
1145-
"the default-constructed object. (N4981 [range.utility.conv.to]/2.1.5)");
1146+
"the default-constructed object. (N5014 [range.utility.conv.to]/2.1.5)");
11461147
}
11471148
} else if constexpr (input_range<range_reference_t<_Rng>>) {
11481149
const auto _Xform = [](auto&& _Elem) _STATIC_LAMBDA {
@@ -1153,15 +1154,15 @@ namespace ranges {
11531154
static_assert(false,
11541155
"ranges::to requires the elements of the source range to be either implicitly convertible to the "
11551156
"elements of the destination container, or be ranges themselves for ranges::to to be applied "
1156-
"recursively. (N4981 [range.utility.conv.to]/2.3)");
1157+
"recursively. (N5014 [range.utility.conv.to]/2.3)");
11571158
}
11581159
}
11591160

11601161
template <class _Container>
11611162
struct _To_class_fn {
11621163
_STL_INTERNAL_STATIC_ASSERT(!is_const_v<_Container>);
11631164
_STL_INTERNAL_STATIC_ASSERT(!is_volatile_v<_Container>);
1164-
_STL_INTERNAL_STATIC_ASSERT(is_class_v<_Container>);
1165+
_STL_INTERNAL_STATIC_ASSERT(is_class_v<_Container> || is_union_v<_Container>);
11651166
_STL_INTERNAL_STATIC_ASSERT(!view<_Container>);
11661167

11671168
template <input_range _Rng, class... _Types>
@@ -1176,9 +1177,10 @@ namespace ranges {
11761177
_EXPORT_STD template <class _Container, class... _Types>
11771178
requires (!view<_Container>)
11781179
_NODISCARD constexpr auto to(_Types&&... _Args) {
1179-
static_assert(!is_const_v<_Container>, "C must not be const. ([range.utility.conv.adaptors])");
1180-
static_assert(!is_volatile_v<_Container>, "C must not be volatile. ([range.utility.conv.adaptors])");
1181-
static_assert(is_class_v<_Container>, "C must be a class type. ([range.utility.conv.adaptors])");
1180+
static_assert(!is_const_v<_Container>, "C must not be const. (N5014 [range.utility.conv.adaptors]/1)");
1181+
static_assert(!is_volatile_v<_Container>, "C must not be volatile. (N5014 [range.utility.conv.adaptors]/1)");
1182+
static_assert(is_class_v<_Container> || is_union_v<_Container>,
1183+
"C must be a class type. (N5014 [range.utility.conv.adaptors]/1)");
11821184
return _Range_closure<_To_class_fn<_Container>, decay_t<_Types>...>{_STD forward<_Types>(_Args)...};
11831185
}
11841186

tests/std/tests/P1206R7_ranges_to_misc/test.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <memory>
88
#include <optional>
99
#include <ranges>
10+
#include <type_traits>
1011
#include <utility>
1112
#include <vector>
1213

@@ -160,6 +161,60 @@ constexpr bool test_nested_range() {
160161
return true;
161162
}
162163

164+
template <class T, class A = std::allocator<T>>
165+
union union_vector {
166+
std::vector<T, A> vec_;
167+
168+
constexpr union_vector() : vec_() {}
169+
constexpr union_vector(const union_vector& other) : vec_(other.vec_) {}
170+
constexpr union_vector(union_vector&& other) noexcept : vec_(std::move(other.vec_)) {}
171+
172+
template <class U>
173+
requires (!std::same_as<std::remove_cvref_t<U>, union_vector>)
174+
&& (!std::same_as<std::remove_cvref_t<U>, std::vector<T, A>>)
175+
&& requires(U&& u) { std::vector<T, A>(std::forward<U>(u)); }
176+
constexpr explicit union_vector(U&& u) : vec_(std::forward<U>(u)) {}
177+
178+
template <class T1, class T2, class... Ts>
179+
requires requires(T1&& t1, T2&& t2, Ts&&... ts) {
180+
std::vector<T, A>(std::forward<T1>(t1), std::forward<T2>(t2), std::forward<Ts>(ts)...);
181+
}
182+
constexpr union_vector(T1&& t1, T2&& t2, Ts&&... ts)
183+
: vec_(std::forward<T1>(t1), std::forward<T2>(t2), std::forward<Ts>(ts)...) {}
184+
185+
constexpr union_vector& operator=(const union_vector& other) {
186+
vec_ = other.vec_;
187+
return *this;
188+
}
189+
constexpr union_vector& operator=(union_vector&& other)
190+
noexcept(std::is_nothrow_move_assignable_v<std::vector<T, A>>) {
191+
vec_ = std::move(other.vec_);
192+
return *this;
193+
}
194+
195+
constexpr ~union_vector() noexcept {
196+
vec_.~vector();
197+
}
198+
};
199+
200+
template <ranges::input_range R, class A = std::allocator<ranges::range_value_t<R>>>
201+
union_vector(std::from_range_t, R&&, A = A()) -> union_vector<ranges::range_value_t<R>, A>;
202+
203+
constexpr bool test_to_union() {
204+
constexpr int src[]{42, 1729};
205+
206+
assert(ranges::equal(ranges::to<union_vector<long>>(src).vec_, src));
207+
assert(ranges::equal((src | ranges::to<union_vector<long>>()).vec_, src));
208+
209+
static_assert(std::same_as<decltype(ranges::to<union_vector>(src)), union_vector<int>>);
210+
assert(ranges::equal(ranges::to<union_vector>(src).vec_, src));
211+
212+
static_assert(std::same_as<decltype(src | ranges::to<union_vector>()), union_vector<int>>);
213+
assert(ranges::equal((src | ranges::to<union_vector>()).vec_, src));
214+
215+
return true;
216+
}
217+
163218
struct ContainerLike {
164219
template <std::input_iterator Iter>
165220
constexpr ContainerLike(Iter first, Iter last) : dist(static_cast<std::ptrdiff_t>(ranges::distance(first, last))) {}
@@ -370,6 +425,9 @@ int main() {
370425
test_nested_range();
371426
static_assert(test_nested_range());
372427

428+
test_to_union();
429+
static_assert(test_to_union());
430+
373431
test_lwg3733();
374432
static_assert(test_lwg3733());
375433

0 commit comments

Comments
 (0)