Namespaces
Variants

std::ranges::to

De es.cppreference.net
 
 
Biblioteca de rangos
Adaptadores de rango
 
Definido en el encabezado <ranges>
template< class C, ranges::input_range R, class... Args >
    requires (!ranges::view<C>)
constexpr C to( R&& r, Args&&... args );
(1) (desde C++23)
template< template< class... > class C,
          ranges::input_range R, class... Args >
constexpr auto to( R&& r, Args&&... args );
(2) (desde C++23)
template< class C, class... Args >
    requires (!ranges::view<C>)
constexpr /*range adaptor closure*/ to( Args&&... args );
(3) (desde C++23)
template< template< class... > class C, class... Args >
constexpr /*range adaptor closure*/ to( Args&&... args );
(4) (desde C++23)
Plantillas auxiliares
template< class Container >
constexpr bool /*reservable-container*/ =
    ranges::sized_range<Container> &&
    requires (Container& c, ranges::range_size_t<Container> n)
    {
        c.reserve(n);
        { c.capacity() } -> std::same_as<decltype(n)>;
        { c.max_size() } -> std::same_as<decltype(n)>;
    };
(5) (solo exposición*)
template< class Container, class Reference >
constexpr bool /*container-appendable*/ =
    requires (Container& c, Reference&& ref)
    {
        requires
        (
            requires { c.emplace_back(std::forward<Reference>(ref)); }     ||
            requires { c.push_back(std::forward<Reference>(ref)); }        ||
            requires { c.emplace(c.end(), std::forward<Reference>(ref)); } ||
            requires { c.insert(c.end(), std::forward<Reference>(ref)); }
        );
    };
(6) (solo exposición*)
template< class Reference, class C >
constexpr auto /*container-appender*/( C& c );
(7) (solo exposición*)
template< class R, class T >
concept /*container-compatible-range*/ =
    ranges::input_range<R> &&
    std::convertible_to<ranges::range_reference_t<R>, T>;
(8) (solo exposición*)

Las sobrecargas de la función de conversión de rango construyen un nuevo objeto que no es una vista a partir de un rango fuente como su primer argumento llamando a un constructor que toma un rango, un std::from_range_t constructor de rango etiquetado, un constructor que toma un par iterador-centinela, o insertando al final cada elemento del rango fuente en el objeto construido con argumentos.

1) Construye un objeto de tipo C a partir de los elementos de r de la siguiente manera:
a) Si C no satisface input_range o std::convertible_to<ranges::range_reference_t<R>, ranges::range_value_t<C>> es true:
1) Construyendo un objeto que no es una vista como si se inicializara directamente (pero no con inicialización directa de lista) un objeto de tipo C a partir del rango fuente std::forward<R>(r) y el resto de los argumentos funcionales std::forward<Args>(args)... si std::constructible_from<C, R, Args...> es true.
2) En caso contrario, construyendo un objeto que no es una vista como si se inicializara directamente (pero no con inicialización directa de lista) un objeto de tipo C a partir de la etiqueta de desambiguación adicional std::from_range, el rango fuente std::forward<R>(r) y el resto de los argumentos funcionales std::forward<Args>(args)... si std::constructible_from<C, std::from_range_t, R, Args...> es true.
3) En caso contrario, construyendo un objeto que no es una vista como si se inicializara directamente (pero no con inicialización directa de lista) un objeto de tipo C a partir del par iterador-centinela (ranges::begin(r) como un iterador y ranges::end(r) como centinela, donde el iterador y el centinela tienen el mismo tipo. En otras palabras, el rango fuente debe ser un rango común), y el resto de los argumentos de función std::forward<Args>(args)... si todas las condiciones siguientes son true:
  • ranges::common_range<R>
  • Si std::iterator_traits<ranges::iterator_t<R>>::iterator_category es válido y denota un tipo que satisface std::derived_from<std::input_iterator_tag>
  • std::constructible_from<C, ranges::iterator_t<R>, ranges::sentinel_t<R>, Args...>
4) En caso contrario, construyendo un objeto de rango que no es una vista como si se inicializara directamente (pero no con inicialización directa de lista) un objeto de tipo C a partir del resto de los argumentos de función std::forward<Args>(args)... con la siguiente llamada equivalente después de la construcción:

if constexpr (ranges::sized_range<R> && /*reservable-container*/<C>)
    c.reserve(static_cast<ranges::range_size_t<C>>(ranges::size(r)));
ranges::for_each(r, /*container-appender*/(c));

(hasta C++26)

if constexpr (ranges::approximately_sized_range<R>
            && /*reservable-container*/<C>)
    c.reserve(static_cast<ranges::range_size_t<C>>(ranges::reserve_hint(r)));
ranges::for_each(r, /*container-appender*/(c));

(desde C++26)

Si R satisface sized_range(hasta C++26)approximately_sized_range(desde C++26) y C satisface reservable-container, el objeto construido c de tipo C es capaz de reservar almacenamiento con el tamaño de almacenamiento inicial ranges::size(r)(hasta C++26)ranges::reserve_hint(r)(desde C++26) para evitar asignaciones adicionales durante la inserción de nuevos elementos. Cada elemento de r se añade a c.

Las operaciones anteriores son válidas si ambas condiciones siguientes son true:

b) En caso contrario, la expresión de retorno es equivalente a
to<C>(ranges::ref_view(r) | views::transform([](auto&& elem)
{
    return to<ranges::range_value_t<C>>(std::forward<decltype(elem)>(elem));
}), std::forward<Args>(args)...)

Lo que permite construcciones de rango anidadas dentro del rango si ranges::input_range<ranges::range_reference_t<C>> es true.

En caso contrario, el programa está mal formado.
2) Construye un objeto de tipo deducido a partir de los elementos de r.

Sea /*input-iterator*/ un tipo de solo exposición que satisface LegacyInputIterator:

struct /*input-iterator*/
{
    using iterator_category = std::input_iterator_tag;
    using value_type = ranges::range_value_t<R>;
    using difference_type = std::ptrdiff_t;
    using pointer = std::add_pointer_t<ranges::range_reference_t<R>>;
    using reference = ranges::range_reference_t<R>;
    reference operator*() const;                      // not defined
    pointer operator->() const;                       // not defined
    /*input-iterator*/& operator++();                 // not defined
    /*input-iterator*/ operator++(int);               // not defined
    bool operator==(const /*input-iterator*/&) const; // not defined
};
(solo exposición*)

Sea /*DEDUCE-EXPR*/ definido como sigue:

  • C(std::declval<R>(), std::declval<Args>()...), si esa expresión es válida.
  • En caso contrario,
C(std::from_range, std::declval<R>(),
  std::declval<Args>()...)
, si esa expresión es válida.
  • En caso contrario,
C(std::declval</*input-iterator*/>(),
  std::declval</*input-iterator*/>(),
  std::declval<Args>()...)
, si esa expresión es válida.
  • En caso contrario, el programa está mal formado.

La llamada es equivalente a

to<decltype(/*DEDUCE-EXPR*/)>
  (std::forward<R>(r), std::forward<Args>(args)...)
.
3,4) Retorna un envoltorio de llamada de reenvío perfecto que también es un ObjetoDeCierreDeAdaptadorDeRango.
5) Es true si satisface ranges::sized_range y es elegible para ser reservable.
6) Es true si un elemento de tipo Reference puede ser añadido a Container a través de una llamada a función miembro emplace_back, push_back, emplace o insert.
7) Retorna un objeto función donde una llamada al objeto función retornado es equivalente en expresión a añadir un elemento a un contenedor. La expresión de retorno es equivalente a
return [&c]<class Reference>(Reference&& ref)
{
    if constexpr (requires { c.emplace_back(std::declval<Reference>()); })
        c.emplace_back(std::forward<Reference>(ref));
    else if constexpr (requires { c.push_back(std::declval<Reference>()); })
        c.push_back(std::forward<Reference>(ref));
    else if constexpr (requires { c.emplace(c.end(),
                                            std::declval<Reference>()); })
        c.emplace(c.end(), std::forward<Reference>(ref));
    else
        c.insert(c.end(), std::forward<Reference>(ref));
};
8) Se utiliza en la definición de contenedores para construir un rango de entrada R cuyo tipo de referencia de rango debe ser convertible a T.

Parámetros

r - un objeto de rango fuente
args - lista de los argumentos para ( 1,2 ) construir un rango o ( 3,4 ) vincular a los últimos parámetros del objeto de cierre de adaptador de rango
Requisitos de tipo
-
C debe ser un tipo de clase sin calificadores cv ( 1,3 )

Valor de retorno

1,2) Un objeto no vista construido.
3,4) Un objeto de cierre de adaptador de rango de tipo no especificado, con las siguientes propiedades:

ranges::to tipo de retorno

Objetos miembro

El objeto retornado se comporta como si no tuviera objeto objetivo, y un objeto std::tuple tup construido con std:: tuple < std:: decay_t < Args > ... > ( std:: forward < Args > ( args ) ... ) , excepto que el comportamiento de asignación del objeto retornado no está especificado y los nombres son solo para exposición.

Constructores

El tipo de retorno de ranges::to ( 3,4 ) se comporta como si sus constructores de copia/movimiento realizaran una copia/movimiento miembro a miembro. Es CopyConstructible si todos sus objetos miembro (especificados arriba) son CopyConstructible , y es MoveConstructible en caso contrario.

Función miembro operator()

Dado un objeto G obtenido de una llamada previa a range :: to < /* see below */ > ( args... ) , cuando un glvalue g que designa G es invocado en una expresión de llamada a función g ( r ) , ocurre una invocación del objeto almacenado, como si fuera mediante

  • ranges :: to < /* see below */ > ( r, std :: get < Ns > ( g. tup ) ... ) , donde
  • r es un objeto de rango fuente que debe satisfacer input_range .
  • Ns es un paquete de enteros 0 , 1 , ..., ( sizeof... ( Args ) - 1 ) .
  • g es un lvalue en la expresión de llamada si es un lvalue en la expresión de llamada, y es un rvalue en caso contrario. Por lo tanto std :: move ( g ) ( r ) puede mover los argumentos vinculados a la llamada, donde g ( r ) copiaría.
  • El argumento de plantilla especificado es ( 3 ) C o ( 4 ) el tipo deducido de una plantilla de clase C que no debe satisfacer view .

El programa está mal formado si g tiene tipo calificado volatile.

Excepciones

Solo lanza si la construcción de un objeto no-vista lanza.

Notas

La inserción de elementos en el contenedor puede implicar copia, lo cual puede ser menos eficiente que mover porque se producen referencias lvalue durante la llamada de indirección. Los usuarios pueden optar por usar views:: as_rvalue para adaptar el rango con el fin de que sus elementos siempre produzcan una referencia rvalue durante la llamada de indirección, lo que implica movimiento.

Los paréntesis son obligatorios cuando se utiliza la sintaxis de tubería.

auto vec = r | std::ranges::to<std::vector>;   // Error
auto vec = r | std::ranges::to<std::vector>(); // Correcto
Macro de prueba de características Valor Estándar Característica
__cpp_lib_ranges_to_container 202202L (C++23) std::ranges::to
__cpp_lib_ranges_reserve_hint 202502L (C++26) ranges::approximately_sized_range , ranges::reserve_hint , y cambios en std::ranges::to

Ejemplo

Un enlace de vista previa: Compiler Explorer

#include <boost/container/devector.hpp>
#include <concepts>
#include <initializer_list>
#include <list>
#include <print>
#include <ranges>
#include <regex>
#include <string>
#include <vector>

int main()
{
    auto vec = std::views::iota(1, 5)
             | std::views::transform([](int v){ return v * 2; })
             | std::ranges::to<std::vector>();

    static_assert(std::same_as<decltype(vec), std::vector<int>>);
    std::println("1) {}", vec);

    auto list = vec | std::views::take(3) | std::ranges::to<std::list<double>>();
    std::println("2) {}", list);
}

void ctor_demos()
{
    // 1.a.1) Direct init
    {
        char array[]{'a', 'b', '\0', 'c'};

        // Argument type is convertible to result value type:
        auto str_to = std::ranges::to<std::string>(array);
        // Equivalent to
        std::string str(array);

        // Result type is not an input range:
        auto re_to = std::ranges::to<std::regex>(array);
        // Equivalent to
        std::regex re(array);
    }

    // 1.a.2) from_range ctor
    {
        auto list = {'a', 'b', '\0', 'c'};

        // Argument type is convertible to result value type:
        auto str_to = std::ranges::to<std::string>(list);
        // Equivalent to
        // std::string str(std::from_range, list);

        // Result type is not an input range:
        [[maybe_unused]]
        auto pair_to = std::ranges::to<std::pair<std::from_range_t, bool>>(true);
        // Equivalent to
        std::pair<std::from_range_t, bool> pair(std::from_range, true);
    }

    // 1.a.3) iterator pair ctor
    {
        auto list = {'a', 'b', '\0', 'c'};

        // Argument type is convertible to result value type:
        auto devector_to = std::ranges::to<boost::container::devector<char>>(list);
        // Equivalent to
        boost::container::devector<char> devector(std::ranges::begin(list),
                                                  std::ranges::end(list));

        // Result type is not an input range:
        std::regex re;
        auto it_to = std::ranges::to<std::cregex_iterator>(list, re);
        // Equivalent to
        std::cregex_iterator it(std::ranges::begin(list), std::ranges::end(list), re);
    }
}

Salida:

1) [2, 4, 6, 8]
2) [2, 4, 6]

Informes de defectos

Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares publicados anteriormente de C++.

DR Aplicado a Comportamiento publicado Comportamiento correcto
LWG 3984 C++23 la rama de construcción anidada de ranges::to resultaba en
programa mal formado si R& no modela viewable_range
se hizo bien formado
LWG 4016 C++23 la rama de inserción de contenedor de
ranges::to involucraba uso de iteradores de inserción
reemplazado con adición directa
de elementos al contenedor

Referencias

  • Estándar C++23 (ISO/IEC 14882:2024):
  • 26.5.7 Conversiones de rango [range.utility.conv]