std::ranges::shift_left,std::ranges::shift_right(3)

NAME
std::ranges::shift_left,std::ranges::shift_right - std::ranges::shift_left,std::ranges::shift_right

Synopsis
   Defined in header <algorithm>
   Call signature
   template< std::permutable I, std::sentinel_for<I> S >
   constexpr ranges::subrange<I> (1) (since C++23)
       shift_left( I first, S last, std::iter_difference_t<I> n );
   template< ranges::forward_range R >
   requires std::permutable<ranges::iterator_t<R>> (2) (since C++23)
   constexpr ranges::borrowed_subrange_t<R>
       shift_left( R&& r, ranges::range_difference_t<R> n );
   template< std::permutable I, std::sentinel_for<I> S >
   constexpr ranges::subrange<I> (3) (since C++23)
       shift_right( I first, S last, std::iter_difference_t<I> n );
   template< ranges::forward_range R >
   requires std::permutable<ranges::iterator_t<R>> (4) (since C++23)
   constexpr ranges::borrowed_subrange_t<R>
       shift_right( R&& r, ranges::range_difference_t<R> n );
   Shifts the elements in the range [first, last) or r by n positions. The behavior is
   undefined if [first, last) is not a valid range.
   1) Shifts the elements towards the beginning of the range:
     * If n == 0 || n >= last - first, there are no effects.
     * If n < 0, the behavior is undefined.
     * Otherwise, for every integer i in [0, last - first - n), moves the element
       originally at position first + n + i to position first + i. The moves are
       performed in increasing order of i starting from 0.
   3) Shifts the elements towards the end of the range:
     * If n == 0 || n >= last - first, there are no effects.
     * If n < 0, the behavior is undefined.
     * Otherwise, for every integer i in [0, last - first - n), moves the element
       originally at position first + i to position first + n + i. If I models
       bidirectional_iterator, then the moves are performed in decreasing order of i
       starting from last - first - n - 1.
   2,4) Same as (1) or (3) respectively, but uses r as the range, as if using
   ranges::begin(r) as first and ranges::end(r) as last.
   Elements that are in the original range but not the new range are left in a valid
   but unspecified state.
   The function-like entities described on this page are niebloids, that is:
     * Explicit template argument lists cannot be specified when calling any of them.
     * None of them are visible to argument-dependent lookup.
     * When any of them are found by normal unqualified lookup as the name to the left
       of the function-call operator, argument-dependent lookup is inhibited.
   In practice, they may be implemented as function objects, or with special compiler
   extensions.

Parameters
   first - the beginning of the original range
   last - the end of the original range
   r - the range of elements to shift
   n - the number of positions to shift

Return value
   1,2) {first, /*NEW_LAST*/}, where NEW_LAST is the end of the resulting range and
   equivalent to:
     * first + (last - first - n), if n is less than last - first;
     * first otherwise.
   3,4) {/*NEW_FIRST*/, last}, where NEW_FIRST is the beginning of the resulting range
   and equivalent to:
     * first + n, if n is less than last - first;
     * last otherwise.

Complexity
   1,2) At most ranges::distance(first, last) - n assignments.
   3,4) At most ranges::distance(first, last) - n assignment or swaps.

Notes
   ranges::shift_left / ranges::shift_right has better efficiency on common
   implementations if I models bidirectional_iterator or (better)
   random_access_iterator.
   Implementations (e.g. MSVC STL) may enable vectorization when the iterator type
   models contiguous_iterator and swapping its value type calls neither non-trivial
   special member function nor ADL-found swap.
   Feature-test macro Value Std Feature
   __cpp_lib_shift 202202L (C++23) std::ranges::shift_left and
                        std::ranges::shift_right

Example
// Run this code
 #include <algorithm>
 #include <iostream>
 #include <string>
 #include <type_traits>
 #include <vector>
 struct S
 {
     int value{0};
     bool specified_state{true};
     S(int v = 0) : value{v} {}
     S(S const& rhs) = default;
     S(S&& rhs) { *this = std::move(rhs); }
     S& operator=(S const& rhs) = default;
     S& operator=(S&& rhs)
     {
         if (this != &rhs)
         {
             value = rhs.value;
             specified_state = rhs.specified_state;
             rhs.specified_state = false;
         }
         return *this;
     }
 };
 template<typename T>
 std::ostream& operator<<(std::ostream& os, std::vector<T> const& v)
 {
     for (const auto& s : v)
     {
         if constexpr (std::is_same_v<T, S>)
             s.specified_state ? os << s.value << ' ' : os << ". ";
         else if constexpr (std::is_same_v<T, std::string>)
             os << (s.empty() ? "." : s) << ' ';
         else
             os << s << ' ';
     }
     return os;
 }
 int main()
 {
     std::cout << std::left;
     std::vector<S> a{1, 2, 3, 4, 5, 6, 7};
     std::vector<int> b{1, 2, 3, 4, 5, 6, 7};
     std::vector<std::string> c{"α", "β", "γ", "δ", "ε", "ζ", "η"};
     std::cout << "vector<S> \tvector<int> \tvector<string>\n";
     std::cout << a << " " << b << " " << c << '\n';
     std::ranges::shift_left(a, 3);
     std::ranges::shift_left(b, 3);
     std::ranges::shift_left(c, 3);
     std::cout << a << " " << b << " " << c << '\n';
     std::ranges::shift_right(a, 2);
     std::ranges::shift_right(b, 2);
     std::ranges::shift_right(c, 2);
     std::cout << a << " " << b << " " << c << '\n';
     std::ranges::shift_left(a, 8); // has no effect: n >= last - first
     std::ranges::shift_left(b, 8); // ditto
     std::ranges::shift_left(c, 8); // ditto
     std::cout << a << " " << b << " " << c << '\n';
 // std::ranges::shift_left(a, -3); // UB
 }

Possible output:
 vector<S> vector<int> vector<string>
 1 2 3 4 5 6 7 1 2 3 4 5 6 7 α β γ δ ε ζ η
 4 5 6 7 . . . 4 5 6 7 5 6 7 δ ε ζ η . . .
 . . 4 5 6 7 . 4 5 4 5 6 7 5 . . δ ε ζ η .
 . . 4 5 6 7 . 4 5 4 5 6 7 5 . . δ ε ζ η .

See also
   ranges::move moves a range of elements to a new location
   (C++20) (niebloid)
   ranges::move_backward moves a range of elements to a new location in backwards order
   (C++20) (niebloid)
   ranges::rotate rotates the order of elements in a range
   (C++20) (niebloid)
   shift_left shifts elements in a range
   shift_right (function template)
   (C++20)