std::reverse_copy(3)

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

πŸ’… std::reverse_copy(3) β€” C++ Standard Library, bestie edition ✨

okay so real talk, this is a page from the C++ Standard Library manual, section 3, and it's about std::reverse_copy. yes the name is literally the whole vibe: it copies stuff backwards. icon behavior honestly πŸŽ€


✨ NAME ✨

std::reverse_copy β€” literally std::reverse_copy. that's it, that's the name, no notes πŸ’€


🌸 SYNOPSIS 🌸

so it lives in the header <algorithm> (go say hi to it):

template< class BidirIt, class OutputIt >

OutputIt reverse_copy( BidirIt first, BidirIt last,      (1)
 (constexpr since C++20)

                               OutputIt d_first );
template< class ExecutionPolicy, class BidirIt, class
        ForwardIt >

        ForwardIt reverse_copy( ExecutionPolicy&& policy,        (2) (since
 C++17)
                                BidirIt first, BidirIt last,

                                ForwardIt d_first );

two flavors, both iconic, pick your fighter πŸ‘‘


πŸ¦‹ DESCRIPTION πŸ¦‹

1) so first things first, let N be std::distance(first, last) β€” that's your count, bestie. it copies the elements from the range [first, last) (the source range) into another range of N elements starting at d_first (the destination range), and the way it does it means the destination ends up in reverse order. πŸ”₯

it behaves as if it ran the assignment *(d_first + N - 1 - i) = *(first + i) ^[1] once for every integer i in [0, N).

⚠️ if the source and destination ranges overlap, the behavior is undefined. not "kinda weird", not "probably fine" β€” undefined. do not do it. πŸ™ˆ

2) same as (1), but it runs according to policy. this overload only joins overload resolution 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)

πŸ’œ PARAMETERS πŸ’œ

  • first, last β€” the range of elements to copy. the source squad.
  • d_first β€” the beginning of the destination range. where the backwards magic lands.

🧚 TYPE REQUIREMENTS 🧚

  • BidirIt must meet the requirements of LegacyBidirectionalIterator.
  • OutputIt must meet the requirements of LegacyOutputIterator.
  • ForwardIt must meet the requirements of LegacyForwardIterator.

(no random-access shortcuts, no made-up iterator vibes, it's giving very specific πŸ’…)


⭐ RETURN VALUE ⭐

an output iterator pointing to the element past the last element copied. classic past-the-end energy, we love to see it.


🌈 COMPLEXITY 🌈

exactly N assignments. no more, no less. it ate and left no crumbs.


πŸ”₯ EXCEPTIONS πŸ”₯

the overload with a template parameter named ExecutionPolicy reports errors like this:

  • 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. yeah. that's the dramatic part. πŸ’€
  • for any other ExecutionPolicy, the behavior is implementation-defined.
  • if the algorithm fails to allocate memory, std::bad_alloc is thrown.

πŸŽ€ POSSIBLE IMPLEMENTATION πŸŽ€

also peep the implementations in libstdc++, libc++, and MSVC STL if you're nosy (affectionate).

template<class BidirIt, class OutputIt>
constexpr // since C++20
OutputIt reverse_copy(BidirIt first, BidirIt last, OutputIt d_first)
{
    for (; first != last; ++d_first)
        *d_first = *(--last);
    return d_first;
}

like. look at it. decrementing last first, assigning, incrementing d_first. reverse copy in four lines. main character energy fr fr 🫢


🌸 NOTES 🌸

implementations (e.g. MSVC STL) may enable vectorization when both iterator types satisfy LegacyContiguousIterator and have the same value type, and the value type is TriviallyCopyable. so that's the secret speedrun condition, iykyk.


πŸ’– EXAMPLE πŸ’–

// Run this code

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

int main()
{
    auto print = [](const std::vector<int>& v)
    {
        for (const auto& value : v)
            std::cout << value << ' ';
        std::cout << '\n';
    };

    std::vector<int> v{1, 2, 3};
    print(v);

    std::vector<int> destination(3);
    std::reverse_copy(std::begin(v), std::end(v),
std::begin(destination));
    print(destination);

    std::reverse_copy(std::rbegin(v), std::rend(v),
std::begin(destination));
    print(destination);
}

✨ Output ✨

1 2 3
3 2 1
1 2 3

first line: the original. second: reversed (forward iterators, reverse output). third: reversed again via rbegin/rend, so it flips back to normal. that third line is the plot twist and I'm obsessed 😭


πŸ› DEFECT REPORTS πŸ›

these are behavior-changing defect reports that got applied retroactively to previously published C++ standards. glow-ups, basically:

  • LWG 2074 β€” applied to C++98. as published: for each i, the assignment was *(d_first + N - i) = *(first + i) ^[1]. corrected to: *(d_first + N - 1 - i) = *(first + i) ^[1]. (they fixed the off-by-one, slay)
  • LWG 2150 β€” applied to C++98. as published: only one element was required to be assigned. corrected: the requirement. (vague before, now it's the requirement, periodt)
^1. ^1.0 ^1.1 ^1.2 LegacyOutputIterator is not required to support binary + and -. the usages of + and - here are exposition-only: the actual computation does not need to use them. so don't @ me about operators, they're just how we explain the math 🧚

πŸ¦‹ SEE ALSO πŸ¦‹

  • reverse β€” reverses the order of elements in a range (function template)
  • ranges::reverse_copy (C++20) β€” creates a copy of a range that is reversed (niebloid)

and that's the page!! πŸ’…βœ¨ this gem came to you courtesy of cppreference.com, dated 2024.06.10 β€” go drink some water, then go reverse-copy something (but not into an overlapping range, bestie, I'm begging πŸ™πŸ’–).

πŸ“¦ where this page comes from