diff --git a/stl/inc/__msvc_ranges_to.hpp b/stl/inc/__msvc_ranges_to.hpp index de0fc9baead..a36bbd6df36 100644 --- a/stl/inc/__msvc_ranges_to.hpp +++ b/stl/inc/__msvc_ranges_to.hpp @@ -1106,9 +1106,10 @@ namespace ranges { _EXPORT_STD template requires (!view<_Container>) _NODISCARD constexpr _Container to(_Rng&& _Range, _Types&&... _Args) { - static_assert(!is_const_v<_Container>, "C must not be const. ([range.utility.conv.to])"); - static_assert(!is_volatile_v<_Container>, "C must not be volatile. ([range.utility.conv.to])"); - static_assert(is_class_v<_Container>, "C must be a class type. ([range.utility.conv.to])"); + static_assert(!is_const_v<_Container>, "C must not be const. (N5014 [range.utility.conv.to]/1)"); + static_assert(!is_volatile_v<_Container>, "C must not be volatile. (N5014 [range.utility.conv.to]/1)"); + static_assert(is_class_v<_Container> || is_union_v<_Container>, + "C must be a class type. (N5014 [range.utility.conv.to]/1)"); if constexpr (_Ref_converts<_Rng, _Container>) { if constexpr (constructible_from<_Container, _Rng, _Types...>) { return _Container(_STD forward<_Rng>(_Range), _STD forward<_Types>(_Args)...); @@ -1142,7 +1143,7 @@ namespace ranges { } else { static_assert(false, "ranges::to requires the result to be constructible from the source range, either " "by using a suitable constructor, or by inserting each element of the range into " - "the default-constructed object. (N4981 [range.utility.conv.to]/2.1.5)"); + "the default-constructed object. (N5014 [range.utility.conv.to]/2.1.5)"); } } else if constexpr (input_range>) { const auto _Xform = [](auto&& _Elem) _STATIC_LAMBDA { @@ -1153,7 +1154,7 @@ namespace ranges { static_assert(false, "ranges::to requires the elements of the source range to be either implicitly convertible to the " "elements of the destination container, or be ranges themselves for ranges::to to be applied " - "recursively. (N4981 [range.utility.conv.to]/2.3)"); + "recursively. (N5014 [range.utility.conv.to]/2.3)"); } } @@ -1161,7 +1162,7 @@ namespace ranges { struct _To_class_fn { _STL_INTERNAL_STATIC_ASSERT(!is_const_v<_Container>); _STL_INTERNAL_STATIC_ASSERT(!is_volatile_v<_Container>); - _STL_INTERNAL_STATIC_ASSERT(is_class_v<_Container>); + _STL_INTERNAL_STATIC_ASSERT(is_class_v<_Container> || is_union_v<_Container>); _STL_INTERNAL_STATIC_ASSERT(!view<_Container>); template @@ -1176,9 +1177,10 @@ namespace ranges { _EXPORT_STD template requires (!view<_Container>) _NODISCARD constexpr auto to(_Types&&... _Args) { - static_assert(!is_const_v<_Container>, "C must not be const. ([range.utility.conv.adaptors])"); - static_assert(!is_volatile_v<_Container>, "C must not be volatile. ([range.utility.conv.adaptors])"); - static_assert(is_class_v<_Container>, "C must be a class type. ([range.utility.conv.adaptors])"); + static_assert(!is_const_v<_Container>, "C must not be const. (N5014 [range.utility.conv.adaptors]/1)"); + static_assert(!is_volatile_v<_Container>, "C must not be volatile. (N5014 [range.utility.conv.adaptors]/1)"); + static_assert(is_class_v<_Container> || is_union_v<_Container>, + "C must be a class type. (N5014 [range.utility.conv.adaptors]/1)"); return _Range_closure<_To_class_fn<_Container>, decay_t<_Types>...>{_STD forward<_Types>(_Args)...}; } diff --git a/tests/std/tests/P1206R7_ranges_to_misc/test.cpp b/tests/std/tests/P1206R7_ranges_to_misc/test.cpp index f168fd834be..a378c316bbf 100644 --- a/tests/std/tests/P1206R7_ranges_to_misc/test.cpp +++ b/tests/std/tests/P1206R7_ranges_to_misc/test.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -160,6 +161,60 @@ constexpr bool test_nested_range() { return true; } +template > +union union_vector { + std::vector vec_; + + constexpr union_vector() : vec_() {} + constexpr union_vector(const union_vector& other) : vec_(other.vec_) {} + constexpr union_vector(union_vector&& other) noexcept : vec_(std::move(other.vec_)) {} + + template + requires (!std::same_as, union_vector>) + && (!std::same_as, std::vector>) + && requires(U&& u) { std::vector(std::forward(u)); } + constexpr explicit union_vector(U&& u) : vec_(std::forward(u)) {} + + template + requires requires(T1&& t1, T2&& t2, Ts&&... ts) { + std::vector(std::forward(t1), std::forward(t2), std::forward(ts)...); + } + constexpr union_vector(T1&& t1, T2&& t2, Ts&&... ts) + : vec_(std::forward(t1), std::forward(t2), std::forward(ts)...) {} + + constexpr union_vector& operator=(const union_vector& other) { + vec_ = other.vec_; + return *this; + } + constexpr union_vector& operator=(union_vector&& other) + noexcept(std::is_nothrow_move_assignable_v>) { + vec_ = std::move(other.vec_); + return *this; + } + + constexpr ~union_vector() noexcept { + vec_.~vector(); + } +}; + +template >> +union_vector(std::from_range_t, R&&, A = A()) -> union_vector, A>; + +constexpr bool test_to_union() { + constexpr int src[]{42, 1729}; + + assert(ranges::equal(ranges::to>(src).vec_, src)); + assert(ranges::equal((src | ranges::to>()).vec_, src)); + + static_assert(std::same_as(src)), union_vector>); + assert(ranges::equal(ranges::to(src).vec_, src)); + + static_assert(std::same_as()), union_vector>); + assert(ranges::equal((src | ranges::to()).vec_, src)); + + return true; +} + struct ContainerLike { template constexpr ContainerLike(Iter first, Iter last) : dist(static_cast(ranges::distance(first, last))) {} @@ -370,6 +425,9 @@ int main() { test_nested_range(); static_assert(test_nested_range()); + test_to_union(); + static_assert(test_to_union()); + test_lwg3733(); static_assert(test_lwg3733());