std::uninit...construct_n(3)

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

std::uninitialized_value_construct_n πŸ’–

okay so this is the std::uninit...construct_n(3) page from the C++ Standard Library, and the manual name is literally std::uninitialized_value_construct_n β€” yes bestie it's the same thing twice, we love a consistent queen πŸ’…

✨ NAME ✨

std::uninitialized_value_construct_n β€” and yep, std::uninitialized_value_construct_n again, because the NAME line said it twice and we are not erasing her history. iconic.

🌸 Synopsis 🌸

Defined in header <memory>:

template< class ForwardIt, class Size >
ForwardIt uninitialized_value_construct_n( ForwardIt first, Size n );

(1) (since C++17)

template< class ExecutionPolicy, class ForwardIt, class Size >
ForwardIt uninitialized_value_construct_n( ExecutionPolicy&& policy,
                                           ForwardIt first, Size n );

(2) (since C++17)

πŸŽ€ Parameters πŸŽ€

  • first β€” the beginning of the range of elements to initialize. it's giving starting point energy ✨
  • n β€” the number of elements to initialize. literally the count, bestie πŸ”’
  • policy β€” the execution policy to use. See execution policy for details. πŸ‘‘

πŸ¦‹ Type requirements πŸ¦‹

  • ForwardIt must meet the requirements of LegacyForwardIterator.
  • No increment, assignment, comparison, or indirection through valid instances of ForwardIt may throw exceptions. no cap, they gotta be chill like that πŸ™ˆ

⭐ Return value ⭐

The end of the range of objects β€” i.e., std::next(first, n). so you get the after-party iterator, periodt πŸ’«

πŸ”₯ Complexity πŸ”₯

Linear in n. it scales with n, bestie, simple as that.

πŸ’€ 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.
okay so this is the serious bit: standard policy + thrown exception = std::terminate, and that is not a vibe. other policies are implementation-defined, iykyk 😭

🧚 Possible implementation 🧚

template<class ForwardIt, class Size>
ForwardIt uninitialized_value_construct_n(ForwardIt first, Size n)
{
    using T = typename std::iterator_traits<ForwardIt>::value_type;
    ForwardIt current = first;
    try
    {
        for (; n > 0 ; (void) ++current, --n)
            ::new (const_cast<void*>(static_cast<const volatile void*>(
                std::addressof(*current)))) T();
        return current;
    }
    catch (...)
    {
        std::destroy(first, current);
        throw;
    }
}

πŸ“ Example πŸ“

// Run this code

#include <iostream>
#include <memory>
#include <string>

int main()
{
    struct S { std::string m{"Default value"}; };

    constexpr int n{3};
    alignas(alignof(S)) unsigned char mem[n * sizeof(S)];

    try
    {
        auto first{reinterpret_cast<S*>(mem)};
        auto last = std::uninitialized_value_construct_n(first, n);

        for (auto it{first}; it != last; ++it)
            std::cout << it->m << '\n';

        std::destroy(first, last);
    }
    catch (...)
    {
        std::cout << "Exception!\n";
    }

    // Notice that for "trivial types" the uninitialized_value_construct_n
    // zero-initializes the given uninitialized memory area.
    int v[]{1, 2, 3, 4};
    for (const int i : v)
        std::cout << i << ' ';
    std::cout << '\n';
    std::uninitialized_value_construct_n(std::begin(v), std::size(v));
    for (const int i : v)
        std::cout << i << ' ';
    std::cout << '\n';
}

🌈 Output 🌈

Default value
Default value
Default value
1 2 3 4
0 0 0 0

πŸ‘‘ Defect reports πŸ‘‘

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

  • DR LWG 3870 β€” applied to C++20 β€” behavior as published: this algorithm might create objects on a const storage β€” correct behavior: kept disallowed. the way they retroactively fixed it and kept it disallowed, obsessed 🫢

πŸ’œ See also πŸ’œ

  • uninitialized_value_construct (C++17) β€” constructs objects by value-initialization in an uninitialized area of memory, defined by a range (function template).
  • uninitialized_default_construct_n (C++17) β€” constructs objects by default-initialization in an uninitialized area of memory, defined by a start and a count (function template).
  • ranges::uninitialized_value_construct_n (C++20) β€” constructs objects by value-initialization in an uninitialized area of memory, defined by a start and a count (niebloid).

mwah πŸ’‹ this cute little page came from http://cppreference.com, version/date 2024.06.10, and it's the std::uninit...construct_n(3) manual page. byeee bestie, go construct those values ✨🫢

πŸ“¦ where this page comes from