Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 24 additions & 8 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -1657,22 +1657,38 @@ namespace ranges {
requires indirectly_movable<_It, _Out>
constexpr move_result<_It, _Out> operator()(_It _First, _Se _Last, _Out _Result) const {
_Adl_verify_range(_First, _Last);
auto _UFirst = _Get_unwrapped(_STD move(_First));
const auto _ULast = _Get_unwrapped(_STD move(_Last));
for (; _UFirst != _ULast; ++_UFirst, (void) ++_Result) {
*_Result = _RANGES iter_move(_UFirst);
}
auto _UResult = _Move_unchecked(
_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _STD move(_Result));

_Seek_wrapped(_First, _STD move(_UFirst));
return {_STD move(_First), _STD move(_Result)};
_Seek_wrapped(_First, _STD move(_UResult.in));
return {_STD move(_First), _STD move(_UResult.out)};
}

template <input_range _Rng, weakly_incrementable _Out>
requires indirectly_movable<iterator_t<_Rng>, _Out>
constexpr move_result<borrowed_iterator_t<_Rng>, _Out> operator()(_Rng&& _Range, _Out _Result) const {
return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _STD move(_Result));
auto _First = _RANGES begin(_Range);
auto _UResult = _Move_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range), _STD move(_Result));

_Seek_wrapped(_First, _STD move(_UResult.in));
return {_STD move(_First), _STD move(_UResult.out)};
}
// clang-format on

private:
template <class _It, class _Se, class _Out>
_NODISCARD static constexpr move_result<_It, _Out> _Move_unchecked(_It _First, const _Se _Last, _Out _Result) {
_STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>);
_STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>);
_STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>);
_STL_INTERNAL_STATIC_ASSERT(indirectly_movable<_It, _Out>);

for (; _First != _Last; ++_First, (void) ++_Result) {
*_Result = _RANGES iter_move(_First);
}

return {_STD move(_First), _STD move(_Result)};
}
};

inline constexpr _Move_fn move{_Not_quite_object::_Construct_tag{}};
Expand Down
95 changes: 63 additions & 32 deletions tests/std/include/range_algorithm_support.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ namespace test {
using Value = std::remove_cv_t<Element>;

public:
constexpr explicit proxy_reference(Element& ref) : ref_{ref} {}
constexpr explicit proxy_reference(Element& r) : ref_{r} {}
proxy_reference(proxy_reference const&) = default;

constexpr proxy_reference const& operator=(proxy_reference const& that) const
Expand All @@ -169,14 +169,16 @@ namespace test {
}

// clang-format off
constexpr operator Value() const requires derived_from<Category, input> && copy_constructible<Value> {
constexpr operator Element&() const requires derived_from<Category, input> {
return ref_;
}
// clang-format on

constexpr void operator=(Value const& val) const requires assignable_from<Element&, Value const&> {
ref_ = val;
template <class T>
requires (!std::same_as<std::remove_cvref_t<T>, proxy_reference> && assignable_from<Element&, T>)
constexpr void operator=(T&& val) const {
ref_ = std::forward<T>(val);
}
// clang-format on

template <class Cat, class Elem>
constexpr boolish operator==(proxy_reference<Cat, Elem> that) const requires CanEq<Element, Elem> {
Expand Down Expand Up @@ -204,41 +206,41 @@ namespace test {
}

// clang-format off
friend constexpr boolish operator==(proxy_reference ref, Value const& val) requires CanEq<Element, Value> {
return {ref.ref_ == val};
friend constexpr boolish operator==(proxy_reference r, Value const& val) requires CanEq<Element, Value> {
return {r.ref_ == val};
}
friend constexpr boolish operator==(Value const& val, proxy_reference ref) requires CanEq<Element, Value> {
return {ref.ref_ == val};
friend constexpr boolish operator==(Value const& val, proxy_reference r) requires CanEq<Element, Value> {
return {r.ref_ == val};
}
friend constexpr boolish operator!=(proxy_reference ref, Value const& val) requires CanNEq<Element, Value> {
return {ref.ref_ != val};
friend constexpr boolish operator!=(proxy_reference r, Value const& val) requires CanNEq<Element, Value> {
return {r.ref_ != val};
}
friend constexpr boolish operator!=(Value const& val, proxy_reference ref) requires CanNEq<Element, Value> {
return {ref.ref_ != val};
friend constexpr boolish operator!=(Value const& val, proxy_reference r) requires CanNEq<Element, Value> {
return {r.ref_ != val};
}
friend constexpr boolish operator<(Value const& val, proxy_reference ref) requires CanLt<Value, Element> {
return {val < ref.ref_};
friend constexpr boolish operator<(Value const& val, proxy_reference r) requires CanLt<Value, Element> {
return {val < r.ref_};
}
friend constexpr boolish operator<(proxy_reference ref, Value const& val) requires CanLt<Element, Value> {
return {ref.ref_ < val};
friend constexpr boolish operator<(proxy_reference r, Value const& val) requires CanLt<Element, Value> {
return {r.ref_ < val};
}
friend constexpr boolish operator>(Value const& val, proxy_reference ref) requires CanGt<Value, Element> {
return {val > ref.ref_};
friend constexpr boolish operator>(Value const& val, proxy_reference r) requires CanGt<Value, Element> {
return {val > r.ref_};
}
friend constexpr boolish operator>(proxy_reference ref, Value const& val) requires CanGt<Element, Value> {
return {ref.ref_ > val};
friend constexpr boolish operator>(proxy_reference r, Value const& val) requires CanGt<Element, Value> {
return {r.ref_ > val};
}
friend constexpr boolish operator<=(Value const& val, proxy_reference ref) requires CanLtE<Value, Element> {
return {val <= ref.ref_};
friend constexpr boolish operator<=(Value const& val, proxy_reference r) requires CanLtE<Value, Element> {
return {val <= r.ref_};
}
friend constexpr boolish operator<=(proxy_reference ref, Value const& val) requires CanLtE<Element, Value> {
return {ref.ref_ <= val};
friend constexpr boolish operator<=(proxy_reference r, Value const& val) requires CanLtE<Element, Value> {
return {r.ref_ <= val};
}
friend constexpr boolish operator>=(Value const& val, proxy_reference ref) requires CanGtE<Value, Element> {
return {val >= ref.ref_};
friend constexpr boolish operator>=(Value const& val, proxy_reference r) requires CanGtE<Value, Element> {
return {val >= r.ref_};
}
friend constexpr boolish operator>=(proxy_reference ref, Value const& val) requires CanGtE<Element, Value> {
return {ref.ref_ >= val};
friend constexpr boolish operator>=(proxy_reference r, Value const& val) requires CanGtE<Element, Value> {
return {r.ref_ >= val};
}
// clang-format on

Expand All @@ -247,6 +249,35 @@ namespace test {
}
};

template <class Ref>
struct common_reference {
Ref ref_;

common_reference(Ref r) : ref_{static_cast<Ref>(r)} {}

// clang-format off
template <class Cat, class Elem>
requires convertible_to<Elem&, Ref>
common_reference(proxy_reference<Cat, Elem> pref) : ref_{pref.peek()} {}
// clang-format on
};
} // namespace test

// clang-format off
template <class Cat, class Elem, class U, template <class> class TQuals, template <class> class UQuals>
requires std::common_reference_with<Elem&, UQuals<U>>
struct std::basic_common_reference<::test::proxy_reference<Cat, Elem>, U, TQuals, UQuals> {
using type = common_reference_t<Elem&, UQuals<U>>;
};

template <class T, class Cat, class Elem, template <class> class TQuals, template <class> class UQuals>
requires std::common_reference_with<TQuals<T>, Elem&>
struct std::basic_common_reference<T, ::test::proxy_reference<Cat, Elem>, TQuals, UQuals> {
using type = common_reference_t<TQuals<T>, Elem&>;
};
// clang-format on

namespace test {
// clang-format off
template <class Category, class Element,
// Model sized_sentinel_for along with sentinel?
Expand Down Expand Up @@ -964,11 +995,11 @@ struct get_nth_fn {
{ return get<I>(std::forward<T>(t)); }

template <class T, class Elem>
[[nodiscard]] constexpr decltype(auto) operator()(test::proxy_reference<T, Elem> ref) const noexcept
[[nodiscard]] constexpr decltype(auto) operator()(test::proxy_reference<T, Elem> r) const noexcept
requires requires {
(*this)(ref.peek());
(*this)(r.peek());
}
{ return (*this)(ref.peek()); }
{ return (*this)(r.peek()); }
};
inline constexpr get_nth_fn<0> get_first;
inline constexpr get_nth_fn<1> get_second;
100 changes: 46 additions & 54 deletions tests/std/tests/P0896R4_ranges_alg_move/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,75 +9,67 @@

#include <range_algorithm_support.hpp>

using namespace std;

struct int_wrapper {
int val = 10;
constexpr int_wrapper() = default;
constexpr int_wrapper(int x) : val{x} {}
constexpr int_wrapper(int_wrapper&& that) : val{std::exchange(that.val, -1)} {}
constexpr int_wrapper(int_wrapper&& that) : val{exchange(that.val, -1)} {}
constexpr int_wrapper& operator=(int_wrapper&& that) {
val = std::exchange(that.val, -1);
val = exchange(that.val, -1);
return *this;
}
auto operator<=>(const int_wrapper&) const = default;
};

constexpr void smoke_test() {
using ranges::move, ranges::move_result, ranges::iterator_t;
using std::same_as;
// Validate that move_result aliases in_out_result
STATIC_ASSERT(same_as<ranges::move_result<int, double>, ranges::in_out_result<int, double>>);

// Validate that move_result aliases in_out_result
STATIC_ASSERT(same_as<move_result<int, double>, ranges::in_out_result<int, double>>);
// Validate dangling story
STATIC_ASSERT(same_as<decltype(ranges::move(borrowed<false>{}, static_cast<int*>(nullptr))),
ranges::move_result<ranges::dangling, int*>>);
STATIC_ASSERT(
same_as<decltype(ranges::move(borrowed<true>{}, static_cast<int*>(nullptr))), ranges::move_result<int*, int*>>);

// Validate dangling story
STATIC_ASSERT(
same_as<decltype(move(borrowed<false>{}, static_cast<int*>(nullptr))), move_result<ranges::dangling, int*>>);
STATIC_ASSERT(same_as<decltype(move(borrowed<true>{}, static_cast<int*>(nullptr))), move_result<int*, int*>>);
struct instantiator {
static constexpr int_wrapper expected_output[3] = {13, 55, 12345};
static constexpr int_wrapper expected_input[3] = {-1, -1, -1};

int const input[] = {13, 53, 12435};
{
int output[] = {-2, -2, -2};
auto result = move(basic_borrowed_range{input}, basic_borrowed_range{output}.begin());
STATIC_ASSERT(same_as<decltype(result),
move_result<iterator_t<basic_borrowed_range<int const>>, iterator_t<basic_borrowed_range<int>>>>);
assert(result.in == basic_borrowed_range{input}.end());
assert(result.out == basic_borrowed_range{output}.end());
assert(ranges::equal(output, input));
static constexpr void eq(int_wrapper const (&output)[3], int_wrapper const (&input)[3]) {
// Extracted into a separate function to keep /analyze from exhausting the compiler heap
assert(ranges::equal(output, expected_output));
assert(ranges::equal(input, expected_input));
}
{
int output[] = {-2, -2, -2};
basic_borrowed_range wrapped_input{input};
auto result = move(wrapped_input.begin(), wrapped_input.end(), basic_borrowed_range{output}.begin());
STATIC_ASSERT(same_as<decltype(result),
move_result<iterator_t<basic_borrowed_range<int const>>, iterator_t<basic_borrowed_range<int>>>>);
assert(result.in == wrapped_input.end());
assert(result.out == basic_borrowed_range{output}.end());
assert(ranges::equal(output, input));
}
{
int_wrapper input1[3] = {13, 55, 1234};
int const expected_output[3] = {13, 55, 1234};
int_wrapper actual_output[3] = {-2, -2, -2};
basic_borrowed_range wrapped_input{input1};
auto result = move(wrapped_input.begin(), wrapped_input.end(), basic_borrowed_range{actual_output}.begin());
assert(result.in == wrapped_input.end());
assert(result.out == basic_borrowed_range{actual_output}.end());
for (int i = 0; i < 3; ++i) {
assert(input1[i].val == -1);
assert(actual_output[i].val == expected_output[i]);
}
}
}

int main() {
STATIC_ASSERT((smoke_test(), true));
smoke_test();
}
template <ranges::input_range Read, indirectly_writable<ranges::range_rvalue_reference_t<Read>> Write>
static constexpr void call() {
using ranges::move, ranges::move_result, ranges::iterator_t;
{
int_wrapper input[3] = {13, 55, 12345};
int_wrapper output[3] = {-2, -2, -2};
Read wrapped_input{input};

struct instantiator {
template <class In, class Out>
static void call(In&& in = {}, Out out = {}) {
(void) ranges::move(in, std::move(out));
(void) ranges::move(ranges::begin(in), ranges::end(in), std::move(out));
auto result = move(wrapped_input, Write{output});
STATIC_ASSERT(same_as<decltype(result), move_result<iterator_t<Read>, Write>>);
assert(result.in == wrapped_input.end());
assert(result.out.peek() == output + 3);
eq(output, input);
}
{
int_wrapper input[3] = {13, 55, 12345};
int_wrapper output[3] = {-2, -2, -2};
Read wrapped_input{input};

auto result = move(wrapped_input.begin(), wrapped_input.end(), Write{output});
assert(result.in == wrapped_input.end());
assert(result.out.peek() == output + 3);
eq(output, input);
}
}
};

template void test_in_write<instantiator, const int, int>();
int main() {
STATIC_ASSERT((test_in_write<instantiator, int_wrapper, int_wrapper>(), true));
test_in_write<instantiator, int_wrapper, int_wrapper>();
}