std::uninitialized_copy_n(3)

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

πŸ’– std::uninitialized_copy_n πŸ’–

okay bestie, welcome to the std::uninitialized_copy_n(3) page from the C++ Standard Libary manual. this one is a C++ memory utility, and we're gonna read it together like a group chat. no skipped deets, no missing flags, periodt. 🫢


πŸ’– NAME πŸ’–

std::uninitialized_copy_n β€” std::uninitialized_copy_n

(Yeah, the name is the name. Iconic.)

✨ Synopsis ✨

Defined in header <memory>.

  • (1) β€” since C++11:
template< class InputIt, class Size, class NoThrowForwardIt >
NoThrowForwardIt uninitialized_copy_n( InputIt first, Size count,
                                        NoThrowForwardIt d_first );
  • (2) β€” since C++17:
template< class ExecutionPolicy, class ForwardIt,
          class Size, class NoThrowForwardIt >
NoThrowForwardIt uninitialized_copy_n( ExecutionPolicy&& policy,
                                        ForwardIt first, Size count,
                                        NoThrowForwardIt d_first );

1) This one copies count elements from a range that starts at first into an uninitialized memory area starting at d_first, as if by:

for (; n > 0; ++d_first, (void) ++first, --n)
     ::new (static_cast<void*>(std::addressof(*d_first)))
        typename std::iterator_traits<NoThrowForwardIt>::value_type(*first);

If an exception is thrown during the initialization, the objects that were already constructed are destroyed in an unspecified order. 😭

⚠️ If d_first + [0, n) overlaps with first + [0, n), the behavior is undefined (since C++20). That is a hard no-bestie zone, fr fr.

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), or if std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>> is true (since C++20).

πŸŽ€ Parameters πŸŽ€

  • first β€” the beginning of the range of the elements to copy.
  • count β€” the number of elements to copy.
  • d_first β€” the beginning of the destination range.
  • policy β€” the execution policy to use. See execution policy for details.

πŸ¦‹ Type requirements πŸ¦‹

  • InputIt must meet the requirements of LegacyInputIterator.
  • ForwardIt must meet the requirements of LegacyForwardIterator.
  • NoThrowForwardIt must meet the requirements of LegacyForwardIterator.
  • No increment, assignment, comparison, or indirection through valid instances of NoThrowForwardIt may throw exceptions.

🌸 Return value 🌸

Iterator to the element past the last element copied. slay. ✨

⭐ Complexity ⭐

Linear in count.

πŸ’€ 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. For any other ExecutionPolicy, the behavior is implementation-defined.
  • If the algorithm fails to allocate memory, std::bad_alloc is thrown.

🧚 Possible implementation 🧚

template<class InputIt, class Size, class NoThrowForwardIt>
NoThrowForwardIt uninitialized_copy_n(InputIt first, Size count,
NoThrowForwardIt d_first) {
    using T = typename std::iterator_traits<NoThrowForwardIt>::value_type;
    NoThrowForwardIt current = d_first;
    try
    {
        for (; count > 0; ++first, (void) ++current, --count)
            ::new (static_cast<void*>(std::addressof(*current))) T(*first);
    }
    catch (...)
    {
        for (; d_first != current; ++d_first)
            d_first->~T();
        throw;
    }
    return current; }

πŸ“ Example πŸ“

// Run this code

#include <algorithm>
#include <iostream>
#include <memory>
#include <string>
#include <tuple>
#include <vector>

int main()
{
    std::vector<std::string> v = {"This", "is", "an", "example"};

    std::string* p;
    std::size_t sz;
    std::tie(p, sz) = std::get_temporary_buffer<std::string>(v.size());
    sz = std::min(sz, v.size());

    std::uninitialized_copy_n(v.begin(), sz, p);

    for (std::string* i = p; i != p + sz; ++i)
    {
        std::cout << *i << ' ';
        i->~basic_string<char>();
    }
    std::cout << '\n';

    std::return_temporary_buffer(p);
}

🌈 Possible output 🌈

This is an example

πŸ’… Defect reports πŸ’…

The following behavior-changing defect reports were applied retroactively to previously published C++ standards. (Receipts, bestie. 🧾)

  • LWG 2133 β€” applied to C++98.
    • Behavior as published: the effect description used a for loop with the iteration expression ++d_first, ++first, --n, which results in argument-dependent lookups of operator,.
    • Correct behavior: discards the value of one operand to disable those ADLs.
  • LWG 2433 β€” applied to C++11.
    • Behavior as published: this algorithm might be hijacked by overloaded operator&.
    • Correct behavior: uses std::addressof.
  • LWG 3870 β€” applied to C++20.
    • Behavior as published: this algorithm might create objects on a const storage.
    • Correct behavior: kept disallowed.

πŸ”₯ See also πŸ”₯

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

okay that's the whole page, bestie πŸ’– source/version and date: http://cppreference.com, 2024.06.10 β€” std::uninitialized_copy_n(3) signing off πŸ«ΆπŸ‘‘

πŸ“¦ where this page comes from