std::uninitialized_fill(3)

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

πŸ’… std::uninitialized_fill(3) β€” C++ Standard Libary bestie briefing πŸ’…

okay so welcome to the std::uninitialized_fill(3) page from the C++ Standard Libary β€” header spelling and all 😭. Grab your iced coffee, bestie, because this one is lowkey iconic: it fills raw, uninitialized memory with a value and somehow makes exception safety look cute. 🫢


🌸 NAME 🌸

std::uninitialized_fill - std::uninitialized_fill β€” that’s the name, bestie, no plot twist πŸ’–


✨ Synopsis ✨

Defined in header <memory>

template< class ForwardIt, class T >
void uninitialized_fill( ForwardIt first, ForwardIt last, const T& value );

1) okay so this one copies the given value into an uninitialized memory area, defined by the range [first, last), as if by:

for (; first != last; ++first)
    ::new (/* VOIDIFY */(*first))
        typename std::iterator_traits<ForwardIt>::value_type(value);

where /* VOIDIFY */(e) is literally:

static_cast<void*>(&e)                (until C++11)
static_cast<void*>(std::addressof(e)) (since C++11)

If an exception gets thrown during the initialization, the objects that were already constructed are destroyed in an unspecified order β€” no promises about order, bestie 😭.

template< class ExecutionPolicy, class ForwardIt, class T >
void uninitialized_fill( ExecutionPolicy&& policy,
                         ForwardIt first, ForwardIt last, const T& value );

2) (since C++17) 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)


πŸŽ€ Parameters πŸŽ€

  • first, last β€” the range of the elements to initialize 🌱
  • value β€” the value to construct the elements with ✨
  • 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. Applying &* to a ForwardIt value must yield a pointer to its value type. (until C++11)

πŸ’« Return value πŸ’«

(none) β€” literally nothing comes back, bestie 🫢


⚑ Complexity ⚑

Linear in the distance between first and last. Slay, but make it O(n) πŸ’…


😭 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 🧚

template<class ForwardIt, class T>
void uninitialized_fill(ForwardIt first, ForwardIt last, const T& value)
{
    using V = typename std::iterator_traits<ForwardIt>::value_type;
    ForwardIt current = first;
    try
    {
        for (; current != last; ++current)
            ::new (static_cast<void*>(std::addressof(*current))) V(value);
    }
    catch (...)
    {
        for (; first != current; ++first)
            first->~V();
        throw;
    }
}

πŸ† Example πŸ†

Run this code, bestie β€” it’s giving β€œmanual object lifetime management but make it fashion” πŸ’–

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

int main()
{
    const std::size_t sz = 4;
    std::allocator<std::string> alloc;
    std::string* p = alloc.allocate(sz);

    std::uninitialized_fill(p, p + sz, "Example");

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

    alloc.deallocate(p, sz);
}

πŸ’» Output πŸ’»

Example
Example
Example
Example

πŸ“ Defect reports πŸ“

The following behavior-changing defect reports were applied retroactively to previously published C++ standards β€” yes, the lore got patched πŸ”₯

  • LWG 866 β€” applied to C++98. Behavior as published: given T as the value type of ForwardIt, if T::operator new exists, the program might be ill-formed. Correct behavior: uses global replacement-new instead.
  • 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_fill_n β€” copies an object to an uninitialized area of memory, defined by a start and a count (function template)
  • ranges::uninitialized_fill (C++20) β€” copies an object to an uninitialized area of memory, defined by a range (niebloid)

Source drop: http://cppreference.com, version dated 2024.06.10 β€” bye bestie, go fill that uninitialized memory responsibly πŸ’–βœ¨

πŸ“¦ where this page comes from