std::ranges...copy_result(3)

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

std::ranges::uninitialized_copy, std::ranges::uninitialized_copy_result πŸŽ€

from the C++ Standard Library manual, page std::ranges...copy_result(3) β€” okay bestie, this one is lowkey iconic and we are getting into it πŸ’…


πŸ’– NAME πŸ’–

It's std::ranges::uninitialized_copy, std::ranges::uninitialized_copy_result β€” yes, both of them, exactly like the manual's NAME line says:

std::ranges::uninitialized_copy,std::ranges::uninitialized_copy_result - std::ranges::uninitialized_copy,std::ranges::uninitialized_copy_result


✨ Synopsis ✨

Defined in header <memory> 🫢

Call signature πŸ’«

(1) β€” since C++20

template< std::input_iterator I, std::sentinel_for<I> S1,
          no-throw-forward-iterator O, no-throw-sentinel-for<O> S2 >
requires std::constructible_from<std::iter_value_t<O>,
                                 std::iter_reference_t<I>>
uninitialized_copy_result<I, O>
uninitialized_copy( I ifirst, S1 ilast, O ofirst, S2 olast );

(2) β€” since C++20

template< ranges::input_range IR, no-throw-forward-range OR >
requires std::constructible_from<ranges::range_value_t<OR>,
                                 ranges::range_reference_t<IR>>
uninitialized_copy_result<ranges::borrowed_iterator_t<IR>,
                          ranges::borrowed_iterator_t<OR>>
uninitialized_copy( IR&& in_range, OR&& out_range );

Helper types 🧚

(3) β€” since C++20

template< class I, class O >
using uninitialized_copy_result = ranges::in_out_result<I, O>;

So (1) is giving: let N be ranges::min(ranges::distance(ifirst, ilast), ranges::distance(ofirst, olast)). It constructs N elements in the output range [ofirst, olast) β€” which is an uninitialized memory area β€” from the elements in the input range [ifirst, ilast). The input and output ranges must not overlap. If an exception is thrown during the initialization, the objects already constructed are destroyed in an unspecified order. The function has the effect equal to:

for (; !(ifirst == ilast || ofirst == olast); ++ofirst, ++ifirst)
{
    ::new (static_cast<void*>(std::addressof(*ofirst)))
        std::remove_reference_t<std::iter_reference_t<O>>(*ifirst);
}

(2) is same as (1), but uses in_range as the first range and out_range as the second range, as if using ranges::begin(in_range) as ifirst, ranges::end(in_range) as ilast, ranges::begin(out_range) as ofirst, and ranges::end(out_range) as olast. Slay πŸ’…

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. iykyk ✨


🌸 Parameters 🌸

  • ifirst, ilast β€” iterator-sentinel pair denoting the range of elements to copy from
  • in_range β€” the range of elements to copy from
  • ofirst, olast β€” iterator-sentinel pair denoting the destination range
  • out_range β€” the destination range

πŸ¦‹ Return value πŸ¦‹

{ifirst + N, ofirst + N}

That's the whole vibe, bestie 🫢


πŸ”₯ Complexity πŸ”₯

\(\scriptsize\mathcal{O}(N)\)π“ž(N).

Literally linear time, periodt πŸ’…


πŸ’€ Exceptions πŸ’€

The exception thrown on construction of the elements in the destination range, if any. Not me being dramatic, but if it throws, it throws 😭


⭐ Notes ⭐

An implementation may improve the efficiency of ranges::uninitialized_copy if the value type of the output range is TrivialType. Nice little optimization moment πŸ‘‘


πŸ‘‘ Possible implementation πŸ‘‘

struct uninitialized_copy_fn
{
    template<std::input_iterator I, std::sentinel_for<I> S1,
             no-throw-forward-iterator O, no-throw-sentinel-for<O> S2>
    requires std::constructible_from<std::iter_value_t<O>, std::iter_reference_t<I>>
    ranges::uninitialized_copy_result<I, O>
    operator()(I ifirst, S1 ilast, O ofirst, S2 olast) const
    {
        O current{ofirst};
        try
        {
            for (; !(ifirst == ilast or current == olast); ++ifirst, ++current)
                ranges::construct_at(std::addressof(*current), *ifirst);
            return {std::move(ifirst), std::move(current)};
        }
        catch (...) // rollback: destroy constructed elements
        {
            for (; ofirst != current; ++ofirst)
                ranges::destroy_at(std::addressof(*ofirst));
            throw;
        }
    }

    template<ranges::input_range IR, no-throw-forward-range OR>
    requires std::constructible_from<ranges::range_value_t<OR>,
                                     ranges::range_reference_t<IR>>
    ranges::uninitialized_copy_result<ranges::borrowed_iterator_t<IR>,
                                      ranges::borrowed_iterator_t<OR>>
    operator()(IR&& in_range, OR&& out_range) const
    {
        return (*this)(ranges::begin(in_range), ranges::end(in_range),
                       ranges::begin(out_range), ranges::end(out_range));
    }
};

inline constexpr uninitialized_copy_fn uninitialized_copy{};

🌈 Example 🌈

// Run this code

#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <memory>
#include <string>

int main()
{
    const char* v[]{"This", "is", "an", "example"};

    if (const auto sz{std::size(v)};
        void* pbuf = std::aligned_alloc(alignof(std::string), sizeof(std::string) * sz))
    {
        try
        {
            auto first{static_cast<std::string*>(pbuf)};
            auto last{first + sz};
            std::ranges::uninitialized_copy(std::begin(v), std::end(v), first, last);

            std::cout << "{";
            for (auto it{first}; it != last; ++it)
                std::cout << (it == first ? "" : ", ") << std::quoted(*it);
            std::cout << "};\n";

            std::ranges::destroy(first, last);
        }
        catch (...)
        {
            std::cout << "uninitialized_copy exception\n";
        }
        std::free(pbuf);
    }
}

Output:

{"This", "is", "an", "example"};

Ate and left no crumbs πŸ“


πŸŽ€ Defect reports πŸŽ€

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

  • LWG 3870 β€” applied to C++20. Behavior as published: this algorithm might create objects on a const storage. Correct behavior: kept disallowed. The way that got handled πŸ’€

πŸ‘€ See also πŸ‘€

  • ranges::uninitialized_copy_n (C++20) β€” copies a number of objects to an uninitialized area of memory (niebloid)
  • uninitialized_copy β€” copies a range of objects to an uninitialized area of memory (function template)

That's the whole page, bestie πŸ’– Source: http://cppreference.com β€” version/date: 2024.06.10 β€” manual: std::ranges...copy_result(3). Now go construct those objects and live your main character era πŸ§šβ€β™€οΈπŸ’œ

πŸ“¦ where this page comes from