◐ Shell
clean mode source ↗

std::ranges::uninitialized_default_construct - cppreference.com

De cppreference.com

Definido en el archivo de encabezado <memory>

Signatura de la llamada

template <no-throw-forward-iterator I, no-throw-sentinel-for<I> S> requires std::default_initializable<std::iter_value_t<I>> I uninitialized_default_construct( I first, S last );

(1) (desde C++20)

template <no-throw-forward-range R> requires std::default_initializable<ranges::range_value_t<R>> ranges::borrowed_iterator_t<R> uninitialized_default_construct( R&& r );

(2) (desde C++20)

1) Construye objetos de tipo std::iter_value_t<I> en el almacenamiento no inicializado designado por el rango [first, last) por la inicialización por defecto, como si fuera por

for (; first != last; ++first)
  ::new (const_cast<void*>(static_cast<const volatile void*>(std::addressof(*first))))
      std::remove_reference_t<std::iter_reference_t<I>>;

Si se lanza una excepción durante la inicialización, los objetos ya construidos se destruyen en un orden no especificado.

2) Igual que (1), pero usa r como rango, como si se usara ranges::begin(r) como first, y ranges::end(r) como last.

Las entidades similares a funciones descritas en esta página son niebloids, es decir:

En la práctica, pueden implementarse como objetos función o con extensiones de compilador especiales.

Parámetros

first, last - Par iterador-centinela que denota el rango de los elementos a inicializar.
r - El rango de elementos a inicializar.

Valor de retorno

Un iterador igual a last.

Complejidad

Lineal en la distancia entre first y last.

Excepciones

La excepción lanzada en la construcción de los elementos en el rango de destino, si existe.

Notas

Una implementación puede omitir la construcción de objetos (sin cambiar el efecto observable) si no se llama a un constructor por defecto no trivial mientras se inicializa por defecto un objeto std::iter_value_t<I>, que puede ser detectado por std::is_trivially_default_constructible_v.

Posible implementación

struct uninitialized_default_construct_fn {
    template <no-throw-forward-iterator I, no-throw-sentinel-for<I> S>
    requires std::default_initializable<std::iter_value_t<I>>
    I operator()( I first, S last ) const {
        using ValueType = std::remove_reference_t<std::iter_reference_t<I>>;
        if constexpr (std::is_trivially_default_constructible_v<ValueType>)
            return ranges::next(first, last); // skip initialization
        I rollback {first};
        try {
            for (; !(first == last); ++first)
                ::new (const_cast<void*>(static_cast<const volatile void*>
                        (std::addressof(*first)))) ValueType;
            return first;
        } catch (...) { // rollback: destruir los elementos construidos
            for (; rollback != first; ++rollback)
                ranges::destroy_at(std::addressof(*rollback));
            throw;
        }
    }

    template <no-throw-forward-range R>
    requires std::default_initializable<ranges::range_value_t<R>>
    ranges::borrowed_iterator_t<R>
    operator()( R&& r ) const {
        return (*this)(ranges::begin(r), ranges::end(r));
    }
};

inline constexpr uninitialized_default_construct_fn uninitialized_default_construct{};

Ejemplo

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

int main()
{
    struct S { std::string m{ "▄▀▄▀▄▀▄▀" }; };

    constexpr int n {4};
    alignas(alignof(S)) char out[n * sizeof(S)];

    try
    {
        auto first {reinterpret_cast<S*>(out)};
        auto last {first + n};

        std::ranges::uninitialized_default_construct(first, last);

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

        std::ranges::destroy(first, last);
    }
    catch(...) { std::cout << "¡Excepción!\n"; }

    // Observa que para "tipos triviales" uninitialized_default_construct
    // generalmente no llena con ceros el área de memoria no inicializada dada.
    constexpr char etalon[] { 'A', 'B', 'C', 'D', '\n' };
    char v[] { 'A', 'B', 'C', 'D', '\n' };
    std::ranges::uninitialized_default_construct(std::begin(v), std::end(v));
    if (std::memcmp(v, etalon, sizeof(v)) == 0) {
        std::cout << "  ";
        // Tal vez comportamiento no definido, pendiente CWG 1997:
        // for (const char c : v) { std::cout << c << ' '; }
        for (const char c : etalon) { std::cout << c << ' '; }
    } else {
        std::cout << "Unspecified\n";
    }
}

Posible salida:

1 ▄▀▄▀▄▀▄▀
2 ▄▀▄▀▄▀▄▀
3 ▄▀▄▀▄▀▄▀
4 ▄▀▄▀▄▀▄▀
  A B C D

Véase también