[libc++] Implement ranges::shift_right (#177847)

Implement the `ranges::shift_right` algorithm from
[P2440R1](https://wg21.link/P2440R1).

Fixes #134062
Fixes #105184
This commit is contained in:
Hui 2026-02-14 17:34:44 +00:00 committed by GitHub
parent 02aa032f6f
commit d7a24d30f6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 501 additions and 40 deletions

View File

@ -394,6 +394,8 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_reference_from_temporary`` *unimplemented*
---------------------------------------------------------- -----------------
``__cpp_lib_shift`` ``202202L``
---------------------------------------------------------- -----------------
``__cpp_lib_spanstream`` *unimplemented*
---------------------------------------------------------- -----------------
``__cpp_lib_stacktrace`` *unimplemented*

View File

@ -38,7 +38,7 @@ What's New in Libc++ 23.0.0?
Implemented Papers
------------------
- P2440R1 (partial): ``ranges::iota`` and ``ranges::shift_left`` are supported (`Github <https://llvm.org/PR105184>`__)
- P2440R1: ``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right`` (`Github <https://llvm.org/PR105184>`__)
Improvements and New Features
-----------------------------

View File

@ -46,7 +46,7 @@
"`P2255R2 <https://wg21.link/P2255R2>`__","A type trait to detect reference binding to temporary","2022-02 (Virtual)","|Partial|","","`#105180 <https://github.com/llvm/llvm-project/issues/105180>`__","Implemented the type traits only."
"`P2273R3 <https://wg21.link/P2273R3>`__","Making ``std::unique_ptr`` constexpr","2022-02 (Virtual)","|Complete|","16","`#105182 <https://github.com/llvm/llvm-project/issues/105182>`__",""
"`P2387R3 <https://wg21.link/P2387R3>`__","Pipe support for user-defined range adaptors","2022-02 (Virtual)","|Complete|","19","`#105183 <https://github.com/llvm/llvm-project/issues/105183>`__",""
"`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Partial|","","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__","``ranges::iota`` and ``ranges::shift_left`` are implemented."
"`P2440R1 <https://wg21.link/P2440R1>`__","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","2022-02 (Virtual)","|Complete|","23","`#105184 <https://github.com/llvm/llvm-project/issues/105184>`__",""
"`P2441R2 <https://wg21.link/P2441R2>`__","``views::join_with``","2022-02 (Virtual)","|Complete|","21","`#105185 <https://github.com/llvm/llvm-project/issues/105185>`__",""
"`P2442R1 <https://wg21.link/P2442R1>`__","Windowing range adaptors: ``views::chunk`` and ``views::slide``","2022-02 (Virtual)","","","`#105187 <https://github.com/llvm/llvm-project/issues/105187>`__",""
"`P2443R1 <https://wg21.link/P2443R1>`__","``views::chunk_by``","2022-02 (Virtual)","|Complete|","18","`#105188 <https://github.com/llvm/llvm-project/issues/105188>`__",""

1 Paper # Paper Name Meeting Status First released version GitHub issue Notes
46 `P2255R2 <https://wg21.link/P2255R2>`__ A type trait to detect reference binding to temporary 2022-02 (Virtual) |Partial| `#105180 <https://github.com/llvm/llvm-project/issues/105180>`__ Implemented the type traits only.
47 `P2273R3 <https://wg21.link/P2273R3>`__ Making ``std::unique_ptr`` constexpr 2022-02 (Virtual) |Complete| 16 `#105182 <https://github.com/llvm/llvm-project/issues/105182>`__
48 `P2387R3 <https://wg21.link/P2387R3>`__ Pipe support for user-defined range adaptors 2022-02 (Virtual) |Complete| 19 `#105183 <https://github.com/llvm/llvm-project/issues/105183>`__
49 `P2440R1 <https://wg21.link/P2440R1>`__ ``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right`` 2022-02 (Virtual) |Partial| |Complete| 23 `#105184 <https://github.com/llvm/llvm-project/issues/105184>`__ ``ranges::iota`` and ``ranges::shift_left`` are implemented.
50 `P2441R2 <https://wg21.link/P2441R2>`__ ``views::join_with`` 2022-02 (Virtual) |Complete| 21 `#105185 <https://github.com/llvm/llvm-project/issues/105185>`__
51 `P2442R1 <https://wg21.link/P2442R1>`__ Windowing range adaptors: ``views::chunk`` and ``views::slide`` 2022-02 (Virtual) `#105187 <https://github.com/llvm/llvm-project/issues/105187>`__
52 `P2443R1 <https://wg21.link/P2443R1>`__ ``views::chunk_by`` 2022-02 (Virtual) |Complete| 18 `#105188 <https://github.com/llvm/llvm-project/issues/105188>`__

View File

@ -158,6 +158,7 @@ set(files
__algorithm/ranges_set_symmetric_difference.h
__algorithm/ranges_set_union.h
__algorithm/ranges_shift_left.h
__algorithm/ranges_shift_right.h
__algorithm/ranges_shuffle.h
__algorithm/ranges_sort.h
__algorithm/ranges_sort_heap.h

View File

@ -0,0 +1,76 @@
//===----------------------------------------------------------------------===//
//
// 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___ALGORITHM_RANGES_SHIFT_RIGHT_H
#define _LIBCPP___ALGORITHM_RANGES_SHIFT_RIGHT_H
#include <__algorithm/iterator_operations.h>
#include <__algorithm/shift_right.h>
#include <__config>
#include <__iterator/concepts.h>
#include <__iterator/distance.h>
#include <__iterator/incrementable_traits.h>
#include <__iterator/permutable.h>
#include <__ranges/access.h>
#include <__ranges/concepts.h>
#include <__ranges/subrange.h>
#include <__utility/move.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
_LIBCPP_PUSH_MACROS
#include <__undef_macros>
_LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER >= 23
namespace ranges {
namespace __shift_right {
struct __fn {
template <permutable _Iter, sentinel_for<_Iter> _Sent>
_LIBCPP_HIDE_FROM_ABI static constexpr subrange<_Iter>
operator()(_Iter __first, _Sent __last, iter_difference_t<_Iter> __n) {
auto __ret = std::__shift_right<_RangeAlgPolicy>(std::move(__first), std::move(__last), std::move(__n));
return {std::move(__ret.first), std::move(__ret.second)};
}
template <forward_range _Range>
requires permutable<iterator_t<_Range>>
_LIBCPP_HIDE_FROM_ABI static constexpr borrowed_subrange_t<_Range>
operator()(_Range&& __range, range_difference_t<_Range> __n) {
if constexpr (sized_range<_Range>) {
if (__n >= ranges::distance(__range)) {
auto __iter = ranges::begin(__range);
auto __end = ranges::end(__range);
ranges::advance(__iter, __end);
return {__iter, std::move(__iter)};
}
}
auto __ret = std::__shift_right<_RangeAlgPolicy>(ranges::begin(__range), ranges::end(__range), std::move(__n));
return {std::move(__ret.first), std::move(__ret.second)};
}
};
} // namespace __shift_right
inline namespace __cpo {
inline constexpr auto shift_right = __shift_right::__fn{};
} // namespace __cpo
} // namespace ranges
#endif // _LIBCPP_STD_VER >= 23
_LIBCPP_END_NAMESPACE_STD
_LIBCPP_POP_MACROS
#endif // _LIBCPP___ALGORITHM_RANGES_SHIFT_RIGHT_H

View File

@ -9,11 +9,17 @@
#ifndef _LIBCPP___ALGORITHM_SHIFT_RIGHT_H
#define _LIBCPP___ALGORITHM_SHIFT_RIGHT_H
#include <__algorithm/iterator_operations.h>
#include <__algorithm/move.h>
#include <__algorithm/move_backward.h>
#include <__algorithm/swap_ranges.h>
#include <__assert>
#include <__concepts/derived_from.h>
#include <__config>
#include <__iterator/concepts.h>
#include <__iterator/iterator_traits.h>
#include <__utility/move.h>
#include <__utility/pair.h>
#include <__utility/swap.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@ -27,36 +33,48 @@ _LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER >= 20
template <class _ForwardIterator>
inline _LIBCPP_HIDE_FROM_ABI constexpr _ForwardIterator
shift_right(_ForwardIterator __first,
_ForwardIterator __last,
typename iterator_traits<_ForwardIterator>::difference_type __n) {
template <class _AlgPolicy, class _Iter, class _Sent>
_LIBCPP_HIDE_FROM_ABI constexpr pair<_Iter, _Iter>
__shift_right(_Iter __first, _Sent __last, typename _IterOps<_AlgPolicy>::template __difference_type<_Iter> __n) {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__n >= 0, "Providing a negative shift amount to shift_right is UB");
if (__n == 0) {
return __first;
_Iter __end = _IterOps<_AlgPolicy>::next(__first, __last);
return pair<_Iter, _Iter>(std::move(__first), std::move(__end));
}
if constexpr (__has_random_access_iterator_category<_ForwardIterator>::value) {
decltype(__n) __d = __last - __first;
if (__n >= __d) {
return __last;
using _IterCategory = typename _IterOps<_AlgPolicy>::template __iterator_category<_Iter>;
if constexpr (derived_from<_IterCategory, random_access_iterator_tag>) {
_Iter __end = _IterOps<_AlgPolicy>::next(__first, __last);
auto __size = __end - __first;
if (__n >= __size) {
return pair<_Iter, _Iter>(__end, std::move(__end));
}
_ForwardIterator __m = __first + (__d - __n);
return std::move_backward(__first, __m, __last);
} else if constexpr (__has_bidirectional_iterator_category<_ForwardIterator>::value) {
_ForwardIterator __m = __last;
_Iter __m = __first;
_IterOps<_AlgPolicy>::advance(__m, (__size - __n));
auto __ret = std::__move_backward<_AlgPolicy>(std::move(__first), std::move(__m), __end);
return pair<_Iter, _Iter>(std::move(__ret.second), std::move(__end));
} else if constexpr (derived_from<_IterCategory, bidirectional_iterator_tag>) {
_Iter __end = _IterOps<_AlgPolicy>::next(__first, __last);
if constexpr (sized_sentinel_for<_Sent, _Iter>) {
if (__n >= ranges::distance(__first, __last)) {
return pair<_Iter, _Iter>(__end, std::move(__end));
}
}
_Iter __m = __end;
for (; __n > 0; --__n) {
if (__m == __first) {
return __last;
return pair<_Iter, _Iter>(__end, std::move(__end));
}
--__m;
}
return std::move_backward(__first, __m, __last);
auto __ret = std::__move_backward<_AlgPolicy>(std::move(__first), std::move(__m), __end);
return pair<_Iter, _Iter>(std::move(__ret.second), std::move(__end));
} else {
_ForwardIterator __ret = __first;
_Iter __ret = __first;
for (; __n > 0; --__n) {
if (__ret == __last) {
return __last;
return pair<_Iter, _Iter>(__ret, std::move(__ret));
}
++__ret;
}
@ -64,28 +82,28 @@ shift_right(_ForwardIterator __first,
// We have an __n-element scratch space from __first to __ret.
// Slide an __n-element window [__trail, __lead) from left to right.
// We're essentially doing swap_ranges(__first, __ret, __trail, __lead)
// over and over; but once __lead reaches __last we needn't bother
// to save the values of elements [__trail, __last).
// over and over; but once __lead reaches __end we needn't bother
// to save the values of elements [__trail, __end).
auto __trail = __first;
auto __lead = __ret;
while (__trail != __ret) {
if (__lead == __last) {
std::move(__first, __trail, __ret);
return __ret;
std::__move<_AlgPolicy>(std::move(__first), std::move(__trail), __ret);
return pair<_Iter, _Iter>(__ret, std::move(__lead));
}
++__trail;
++__lead;
}
_ForwardIterator __mid = __first;
_Iter __mid = __first;
while (true) {
if (__lead == __last) {
__trail = std::move(__mid, __ret, __trail);
std::move(__first, __mid, __trail);
return __ret;
__trail = std::__move<_AlgPolicy>(__mid, __ret, __trail).second;
std::__move<_AlgPolicy>(std::move(__first), std::move(__mid), std::move(__trail));
return pair<_Iter, _Iter>(__ret, std::move(__lead));
}
swap(*__mid, *__trail);
_IterOps<_AlgPolicy>::iter_swap(__mid, __trail);
++__mid;
++__trail;
++__lead;
@ -96,6 +114,14 @@ shift_right(_ForwardIterator __first,
}
}
template <class _ForwardIterator>
_LIBCPP_HIDE_FROM_ABI constexpr _ForwardIterator
shift_right(_ForwardIterator __first,
_ForwardIterator __last,
typename iterator_traits<_ForwardIterator>::difference_type __n) {
return std::__shift_right<_ClassicAlgPolicy>(std::move(__first), std::move(__last), __n).first;
}
#endif // _LIBCPP_STD_VER >= 20
_LIBCPP_END_NAMESPACE_STD

View File

@ -1496,6 +1496,13 @@ template<class ForwardIterator>
shift_right(ForwardIterator first, ForwardIterator last,
typename iterator_traits<ForwardIterator>::difference_type n); // C++20
template<permutable I, sentinel_for<I> S>
constexpr subrange<I> ranges::shift_right(I first, S last, iter_difference_t<I> n); // since C++23
template<forward_range R>
requires permutable<iterator_t<R>>
constexpr borrowed_subrange_t<R> ranges::shift_right(R&& r, range_difference_t<R> n) // since C++23
template <class InputIterator, class Predicate>
constexpr bool // constexpr since C++20
is_partitioned(InputIterator first, InputIterator last, Predicate pred);
@ -2043,6 +2050,7 @@ template <class BidirectionalIterator, class Compare>
# include <__algorithm/ranges_find_last.h>
# include <__algorithm/ranges_fold.h>
# include <__algorithm/ranges_shift_left.h>
# include <__algorithm/ranges_shift_right.h>
# include <__algorithm/ranges_starts_with.h>
# endif // _LIBCPP_STD_VER >= 23

View File

@ -764,6 +764,9 @@ module std [system] {
module ranges_shift_left {
header "__algorithm/ranges_shift_left.h"
}
module ranges_shift_right {
header "__algorithm/ranges_shift_right.h"
}
module ranges_shuffle {
header "__algorithm/ranges_shuffle.h"
}

View File

@ -236,7 +236,8 @@ __cpp_lib_shared_ptr_arrays 201707L <memory>
201611L // C++17
__cpp_lib_shared_ptr_weak_type 201606L <memory>
__cpp_lib_shared_timed_mutex 201402L <shared_mutex>
__cpp_lib_shift 201806L <algorithm>
__cpp_lib_shift 202202L <algorithm>
201806L // C++20
__cpp_lib_smart_ptr_for_overwrite 202002L <memory>
__cpp_lib_smart_ptr_owner_equality 202306L <memory>
__cpp_lib_source_location 201907L <source_location>
@ -533,6 +534,8 @@ __cpp_lib_void_t 201411L <type_traits>
# define __cpp_lib_ranges_to_container 202202L
# define __cpp_lib_ranges_zip 202110L
// # define __cpp_lib_reference_from_temporary 202202L
# undef __cpp_lib_shift
# define __cpp_lib_shift 202202L
// # define __cpp_lib_spanstream 202106L
// # define __cpp_lib_stacktrace 202011L
# define __cpp_lib_stdatomic_h 202011L

View File

@ -364,9 +364,11 @@ export namespace std {
using std::shift_right;
#if _LIBCPP_STD_VER >= 23
namespace ranges {
// using std::ranges::shift_right;
using std::ranges::shift_right;
}
#endif // _LIBCPP_STD_VER >= 23
// [alg.sorting], sorting and related operations
// [alg.sort], sorting

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
//
//===----------------------------------------------------------------------===//
// <algorithm>
// REQUIRES: has-unix-headers
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-hardening-mode=none
// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
#include <algorithm>
#include <array>
#include "check_assertion.h"
int main(int, char**) {
std::array<int, 5> arr = {1, 2, 3, 4, 5};
TEST_LIBCPP_ASSERT_FAILURE(
std::ranges::shift_right(arr, -2), "Providing a negative shift amount to shift_right is UB");
TEST_LIBCPP_ASSERT_FAILURE(
std::ranges::shift_right(arr.begin(), arr.end(), -2), "Providing a negative shift amount to shift_right is UB");
return 0;
}

View File

@ -0,0 +1,307 @@
//===----------------------------------------------------------------------===//
//
// 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(has-fconstexpr-steps): -fconstexpr-steps=9000000
// <algorithm>
// template<permutable I, sentinel_for<I> S>
// constexpr subrange<I> ranges::shift_right(I first, S last, iter_difference_t<I> n);
// template<forward_range R>
// requires permutable<iterator_t<R>>
// constexpr borrowed_subrange_t<R> ranges::shift_right(R&& r, range_difference_t<R> n)
#include <algorithm>
#include <array>
#include <cassert>
#include <ranges>
#include <vector>
#include "almost_satisfies_types.h"
#include "test_macros.h"
#include "test_iterators.h"
#include "MoveOnly.h"
struct InvalidDifferenceT {};
template <class Iter, class Sent = Iter, class N = std::iter_difference_t<Iter>>
concept HasShiftRightIt = requires(Iter iter, Sent sent, N n) { std::ranges::shift_right(iter, sent, n); };
static_assert(HasShiftRightIt<int*>);
static_assert(HasShiftRightIt<int*, sentinel_wrapper<int*>>);
static_assert(HasShiftRightIt<int*, sized_sentinel<int*>>);
static_assert(!HasShiftRightIt<int*, int*, InvalidDifferenceT>);
static_assert(!HasShiftRightIt<int*, int, int>);
static_assert(!HasShiftRightIt<ForwardIteratorNotDerivedFrom>);
static_assert(!HasShiftRightIt<PermutableNotForwardIterator>);
static_assert(!HasShiftRightIt<PermutableNotSwappable>);
template <class Range, class N = std::ranges::range_difference_t<Range>>
concept HasShiftRightR = requires(Range range, N n) { std::ranges::shift_right(range, n); };
static_assert(HasShiftRightR<UncheckedRange<int*>>);
static_assert(!HasShiftRightR<UncheckedRange<int*>, InvalidDifferenceT>);
static_assert(!HasShiftRightR<ForwardRangeNotDerivedFrom>);
static_assert(!HasShiftRightR<PermutableRangeNotForwardIterator>);
static_assert(!HasShiftRightR<PermutableRangeNotSwappable>);
struct TrackCopyMove {
mutable int copy_count = 0;
int move_count = 0;
constexpr TrackCopyMove() = default;
constexpr TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
++copy_count;
++other.copy_count;
}
constexpr TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
++move_count;
++other.move_count;
}
constexpr TrackCopyMove& operator=(const TrackCopyMove& other) {
++copy_count;
++other.copy_count;
return *this;
}
constexpr TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
++move_count;
++other.move_count;
return *this;
}
};
template <class Iter, class Sent>
constexpr void test_iter_sent() {
{
const std::array<int, 8> original = {3, 1, 4, 1, 5, 9, 2, 6};
// (iterator, sentinel) overload
for (size_t n = 0; n <= original.size(); ++n) {
for (size_t k = 0; k <= n + 2; ++k) {
std::vector<int> scratch(n);
auto begin = Iter(scratch.data());
auto end = Sent(Iter(scratch.data() + n));
std::ranges::copy(original.begin(), original.begin() + n, begin);
std::same_as<std::ranges::subrange<Iter>> decltype(auto) result = std::ranges::shift_right(begin, end, k);
assert(result.end() == end);
if (k < n) {
assert(result.begin() == Iter(scratch.data() + k));
assert(std::ranges::equal(original.begin(), original.begin() + n - k, result.begin(), result.end()));
} else {
assert(result.begin() == end);
assert(std::ranges::equal(original.begin(), original.begin() + n, begin, end));
}
}
}
// (range) overload
for (size_t n = 0; n <= original.size(); ++n) {
for (size_t k = 0; k <= n + 2; ++k) {
std::vector<int> scratch(n);
auto begin = Iter(scratch.data());
auto end = Sent(Iter(scratch.data() + n));
std::ranges::copy(original.begin(), original.begin() + n, begin);
auto range = std::ranges::subrange(begin, end);
std::same_as<std::ranges::subrange<Iter>> decltype(auto) result = std::ranges::shift_right(range, k);
assert(result.end() == end);
if (k < n) {
assert(result.begin() == Iter(scratch.data() + k));
assert(std::ranges::equal(original.begin(), original.begin() + n - k, result.begin(), result.end()));
} else {
assert(result.begin() == end);
assert(std::ranges::equal(original.begin(), original.begin() + n, begin, end));
}
}
}
}
// n == 0
{
std::array<int, 3> input = {0, 1, 2};
const std::array<int, 3> expected = {0, 1, 2};
{ // (iterator, sentinel) overload
auto in = input;
auto begin = Iter(in.data());
auto end = Sent(Iter(in.data() + in.size()));
std::same_as<std::ranges::subrange<Iter>> decltype(auto) result = std::ranges::shift_right(begin, end, 0);
assert(std::ranges::equal(expected, result));
assert(result.begin() == begin);
assert(result.end() == end);
}
{ // (range) overload
auto in = input;
auto begin = Iter(in.data());
auto end = Sent(Iter(in.data() + in.size()));
auto range = std::ranges::subrange(begin, end);
std::same_as<std::ranges::subrange<Iter>> decltype(auto) result = std::ranges::shift_right(range, 0);
assert(std::ranges::equal(expected, result));
assert(result.begin() == begin);
assert(result.end() == end);
}
}
// n == len
{
std::array<int, 3> input = {0, 1, 2};
const std::array<int, 3> expected = {0, 1, 2};
{ // (iterator, sentinel) overload
auto in = input;
auto begin = Iter(in.data());
auto end = Sent(Iter(in.data() + in.size()));
std::same_as<std::ranges::subrange<Iter>> decltype(auto) result =
std::ranges::shift_right(begin, end, input.size());
assert(std::ranges::equal(expected, input));
assert(result.begin() == end);
assert(result.end() == end);
}
{ // (range) overload
auto in = input;
auto begin = Iter(in.data());
auto end = Sent(Iter(in.data() + in.size()));
auto range = std::ranges::subrange(begin, end);
std::same_as<std::ranges::subrange<Iter>> decltype(auto) result = std::ranges::shift_right(range, input.size());
assert(std::ranges::equal(expected, input));
assert(result.begin() == end);
assert(result.end() == end);
}
}
// n > len
{
std::array<int, 3> input = {0, 1, 2};
const std::array<int, 3> expected = {0, 1, 2};
{ // (iterator, sentinel) overload
auto in = input;
auto begin = Iter(in.data());
auto end = Sent(Iter(in.data() + in.size()));
std::same_as<std::ranges::subrange<Iter>> decltype(auto) result =
std::ranges::shift_right(begin, end, input.size() + 1);
assert(std::ranges::equal(expected, input));
assert(result.begin() == end);
assert(result.end() == end);
}
{ // (range) overload
auto in = input;
auto begin = Iter(in.data());
auto end = Sent(Iter(in.data() + in.size()));
auto range = std::ranges::subrange(begin, end);
std::same_as<std::ranges::subrange<Iter>> decltype(auto) result =
std::ranges::shift_right(range, input.size() + 1);
assert(std::ranges::equal(expected, input));
assert(result.begin() == end);
assert(result.end() == end);
}
}
// empty range
{
std::vector<int> input = {};
const std::vector<int> expected = {};
{ // (iterator, sentinel) overload
auto in = input;
auto begin = Iter(in.data());
auto end = Sent(Iter(in.data() + in.size()));
std::same_as<std::ranges::subrange<Iter>> decltype(auto) result =
std::ranges::shift_right(begin, end, input.size() + 1);
assert(std::ranges::equal(expected, input));
assert(result.begin() == begin);
assert(result.end() == begin);
}
{ // (range) overload
auto in = input;
auto begin = Iter(in.data());
auto end = Sent(Iter(in.data() + in.size()));
auto range = std::ranges::subrange(begin, end);
std::same_as<std::ranges::subrange<Iter>> decltype(auto) result =
std::ranges::shift_right(range, input.size() + 1);
assert(std::ranges::equal(expected, input));
assert(result.begin() == begin);
assert(result.end() == begin);
}
}
}
constexpr bool test() {
types::for_each(types::forward_iterator_list<int*>{}, []<class Iter> {
test_iter_sent<Iter, Iter>();
test_iter_sent<Iter, sentinel_wrapper<Iter>>();
test_iter_sent<Iter, sized_sentinel<Iter>>();
});
// Complexity: At most (last - first) - n assignments or swaps.
{
constexpr int length = 100;
constexpr int n = length / 2;
auto make_vec = []() {
std::vector<TrackCopyMove> vec;
vec.reserve(length);
for (int i = 0; i < length; ++i) {
vec.emplace_back();
}
return vec;
};
{ // (iterator, sentinel) overload
auto input = make_vec();
auto result = std::ranges::shift_right(input.begin(), input.end(), n);
assert(result.begin() == input.begin() + length - n);
assert(result.end() == input.end());
auto total_copies = 0;
auto total_moves = 0;
for (auto it = result.begin(); it != result.end(); ++it) {
const auto& item = *it;
total_copies += item.copy_count;
total_moves += item.move_count;
}
assert(total_copies == 0);
assert(total_moves <= length - n);
}
{ // (range) overload
auto input = make_vec();
auto result = std::ranges::shift_right(input, n);
assert(result.begin() == input.begin() + length - n);
assert(result.end() == input.end());
auto total_copies = 0;
auto total_moves = 0;
for (auto it = result.begin(); it != result.end(); ++it) {
const auto& item = *it;
total_copies += item.copy_count;
total_moves += item.move_count;
}
assert(total_copies == 0);
assert(total_moves <= length - n);
}
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -225,6 +225,7 @@ constexpr bool test_all() {
#if TEST_STD_VER >= 23
dangling_1st<out_value_result<dangling, decltype(x)>>(std::ranges::iota, in, x);
dangling_1st(std::ranges::shift_left, in, x);
dangling_1st(std::ranges::shift_right, in, x);
#endif
return true;

View File

@ -193,7 +193,9 @@ constexpr void run_tests() {
test(std::ranges::next_permutation, in);
#if TEST_STD_VER >= 23
test(std::ranges::shift_left, in, count);
test(std::ranges::shift_right, in, count);
#endif
// The algorithms that work on uninitialized memory have constraints that prevent proxy iterators from being used with
// them.
}

View File

@ -348,8 +348,8 @@
# ifndef __cpp_lib_shift
# error "__cpp_lib_shift should be defined in c++23"
# endif
# if __cpp_lib_shift != 201806L
# error "__cpp_lib_shift should have the value 201806L in c++23"
# if __cpp_lib_shift != 202202L
# error "__cpp_lib_shift should have the value 202202L in c++23"
# endif
#elif TEST_STD_VER > 23
@ -452,8 +452,8 @@
# ifndef __cpp_lib_shift
# error "__cpp_lib_shift should be defined in c++26"
# endif
# if __cpp_lib_shift != 201806L
# error "__cpp_lib_shift should have the value 201806L in c++26"
# if __cpp_lib_shift != 202202L
# error "__cpp_lib_shift should have the value 202202L in c++26"
# endif
#endif // TEST_STD_VER > 23

View File

@ -5916,8 +5916,8 @@
# ifndef __cpp_lib_shift
# error "__cpp_lib_shift should be defined in c++23"
# endif
# if __cpp_lib_shift != 201806L
# error "__cpp_lib_shift should have the value 201806L in c++23"
# if __cpp_lib_shift != 202202L
# error "__cpp_lib_shift should have the value 202202L in c++23"
# endif
# ifndef __cpp_lib_smart_ptr_for_overwrite
@ -7868,8 +7868,8 @@
# ifndef __cpp_lib_shift
# error "__cpp_lib_shift should be defined in c++26"
# endif
# if __cpp_lib_shift != 201806L
# error "__cpp_lib_shift should have the value 201806L in c++26"
# if __cpp_lib_shift != 202202L
# error "__cpp_lib_shift should have the value 202202L in c++26"
# endif
# ifndef __cpp_lib_smart_ptr_for_overwrite

View File

@ -157,6 +157,7 @@ static_assert(test(std::ranges::set_symmetric_difference, a, a, a));
static_assert(test(std::ranges::set_union, a, a, a));
#if TEST_STD_VER >= 23
static_assert(test(std::ranges::shift_left, a, 42));
static_assert(test(std::ranges::shift_right, a, 42));
#endif
static_assert(test(std::ranges::shuffle, a, g));
static_assert(test(std::ranges::sort, a));

View File

@ -1270,7 +1270,7 @@ feature_test_macros = [
},
{
"name": "__cpp_lib_shift",
"values": {"c++20": 201806},
"values": {"c++20": 201806, "c++23": 202202},
"headers": ["algorithm"],
},
{