std::rotate(3)

πŸ“¦ version 2024.07.05, in nixpkgs unstable, opensuse-leap 16.0

🌸 NAME 🌸

Hey bestie πŸ’– this is std::rotate(3), straight outta the C++ Standard Libary (yes, that's the spelling in the header β€” we don't fix, we appreciate 🫢). The name section is literally std::rotate - std::rotate, because why be subtle when you can be iconic?


✨ Synopsis ✨

Defined in header <algorithm> πŸ¦‹

template< class ForwardIt >
ForwardIt rotate( ForwardIt first, ForwardIt middle,     (1)
(constexpr since C++20)
ForwardIt last );
template< class ExecutionPolicy, class ForwardIt >

ForwardIt rotate( ExecutionPolicy&& policy,              (2) (since
C++17)

                  ForwardIt first, ForwardIt middle,
ForwardIt last );
  1. Performs a left rotation on a range of elements. Specifically, std::rotate swaps the elements in the range [first, last) in such a way that the elements in [first, middle) are placed after the elements in [middle, last) while the orders of the elements in both ranges are preserved. Order preservation is the whole vibe πŸ’…
  2. Same as (1), but executed according to policy. This overload participates in overload resolution only if:
    • std::is_execution_policy_v<std::decay_t<ExecutionPolicy>> is true. (until C++20)
    • std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>> is true. (since C++20)
🚨 Undefined behavior alert, bestie 🚨 If any of the following conditions is satisfied, the behavior is undefined:
  • [first, middle) or [middle, last) is not a valid range.
  • The type of *first is not Swappable. (until C++11)
  • ForwardIt is not ValueSwappable.
  • The type of *first is not MoveConstructible. (since C++11)
  • The type of *first is not MoveAssignable.

πŸ’– Parameters πŸ’–

  • first β€” the beginning of the original range
  • middle β€” the element that should appear at the beginning of the rotated range
  • last β€” the end of the original range
  • policy β€” the execution policy to use. See execution policy for details.

πŸ¦‹ Type requirements πŸ¦‹

  • ForwardIt must meet the requirements of LegacyForwardIterator.

πŸŽ€ Return value πŸŽ€

The iterator to the element originally referenced by *first, i.e. the std::distance(middle, last)th next iterator of first.


🌈 Complexity 🌈

At most std::distance(first, last) swaps.


πŸ’€ Exceptions πŸ’€

The overload with a template parameter named ExecutionPolicy reports errors as follows:

  • If execution of a function invoked as part of the algorithm throws an exception and ExecutionPolicy is one of the standard policies, std::terminate is called. For any other ExecutionPolicy, the behavior is implementation-defined.
  • If the algorithm fails to allocate memory, std::bad_alloc is thrown.

🧚 Possible implementation 🧚

See also the implementations in libstdc++, libc++, and MSVC STL πŸ‘€

template<class ForwardIt>
constexpr // since C++20
ForwardIt rotate(ForwardIt first, ForwardIt middle, ForwardIt last)
{
    if (first == middle)
        return last;

    if (middle == last)
        return first;

    ForwardIt write = first;
    ForwardIt next_read = first; // read position for when β€œread” hits β€œlast”

    for (ForwardIt read = middle; read != last; ++write, ++read)
    {
        if (write == next_read)
            next_read = read; // track where β€œfirst” went
        std::iter_swap(write, read);
    }

    // rotate the remaining sequence into place
    rotate(write, next_read, last);
    return write;
}

⭐ Notes ⭐

std::rotate has better efficiency on common implementations if ForwardIt satisfies LegacyBidirectionalIterator or (better) LegacyRandomAccessIterator πŸ”₯

Implementations (e.g. MSVC STL) may enable vectorization when the iterator type satisfies LegacyContiguousIterator and swapping its value type calls neither non-trivial special member function nor ADL-found swap.


πŸ“ Example πŸ“

std::rotate is a common building block in many algorithms. This example demonstrates insertion sort.

The original even says // Run this code before the example, so here it is πŸ’»

#include <algorithm>
#include <iostream>
#include <vector>

auto print = [](const auto remark, const auto& v)
{
    std::cout << remark;
    for (auto n : v)
        std::cout << n << ' ';
    std::cout << '\n';
};

int main()
{
    std::vector<int> v{2, 4, 2, 0, 5, 10, 7, 3, 7, 1};
    print("before sort:\t\t", v);

    // insertion sort
    for (auto i = v.begin(); i != v.end(); ++i)
        std::rotate(std::upper_bound(v.begin(), i, *i), i, i + 1);
    print("after sort:\t\t", v);

    // simple rotation to the left
    std::rotate(v.begin(), v.begin() + 1, v.end());
    print("simple rotate left:\t", v);

    // simple rotation to the right
    std::rotate(v.rbegin(), v.rbegin() + 1, v.rend());
    print("simple rotate right:\t", v);
}

πŸ‘‘ Output πŸ‘‘

before sort:            2 4 2 0 5 10 7 3 7 1
after sort:             0 1 2 2 3 4 5 7 7 10
simple rotate left:     1 2 2 3 4 5 7 7 10 0
simple rotate right:    0 1 2 2 3 4 5 7 7 10

πŸ”₯ Defect reports πŸ”₯

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

  • DR LWG 488 Β· applied to C++98 β€” behavior as published: the new location of the element pointed by first was not returned; correct behavior: returned.

πŸ’œ See also πŸ’œ

  • rotate_copy β€” copies and rotate a range of elements (function template)
  • ranges::rotate β€” rotates the order of elements in a range (C++20) (niebloid)

Source/version/date check, bestie: http://cppreference.com, 2024.06.10, std::rotate(3) β€” mwah πŸ’‹βœ¨

πŸ“¦ where this page comes from