Namespaces
Variants

std::ranges:: to

From cppreference.net
Ranges library
Range adaptors
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 /*closure de adaptador de rango*/ to ( Args && ... args ) ;
(3) (desde C++23)
template < template < class ... > class C, class ... Args >
constexpr /*cierre adaptador de rango*/ to ( Args && ... args ) ;
(4) (desde C++23)
Plantillas auxiliares
template < class Container >

constexpr bool /*contenedor-reservable*/ =
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 para 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 para exposición* )
template < class Reference, class C >
constexpr auto /*container-appender*/ ( C & c ) ;
(7) ( solo para 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 para exposición* )

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

1) Construye un objeto de tipo C a partir de los elementos de r de la siguiente manera:
1) Construyendo un objeto no-vista como si se estuviera inicializando directamente (pero no inicializando por lista-directa) un objeto de tipo C desde el 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, construir un objeto no-vista como si se estuviera inicializando directamente (pero no inicializando directamente por lista) un objeto de tipo C desde 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) De lo contrario, construir un objeto no-vista como si se estuviera inicializando directamente (pero no inicializando-directamente-por-lista) un objeto de tipo C desde el par iterador-centinela ( ranges:: begin ( r ) como 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 argumentos de función std:: forward < Args > ( args ) ... si todas las condiciones siguientes son true :
4) En caso contrario, se construye un objeto de rango que no es vista como si se inicializara directamente (pero no inicialización de lista directa) un objeto de tipo C a partir del resto de los argumentos de la 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 puede reservar almacenamiento con el tamaño inicial de almacenamiento 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) De lo 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 cual permite construcciones de rango anidadas dentro del rango si ranges:: input_range < ranges:: range_reference_t < C >> es true .

De lo contrario, el programa está mal formado.
2) Constructs an object of deduced type from the elements of r .

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

struct /*iterador-de-entrada*/

{
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 ; // no definido
pointer operator - > ( ) const ; // no definido
/*iterador-de-entrada*/ & operator ++ ( ) ; // no definido
/*iterador-de-entrada*/ operator ++ ( int ) ; // no definido
bool operator == ( const /*iterador-de-entrada*/ & ) const ; // no definido

} ;
( solo para exposición* )

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

The call is equivalent to a < 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 RangeAdaptorClosureObject .
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 mediante 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 input range R cuyo tipo de referencia de rango debe ser convertible a T .

Contenidos

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>
#ifndef __cpp_lib_format_ranges
#include <format>
#include <sstream>
auto print_aid(const auto& v)
{
    std::ostringstream out;
    out << '[';
    for (int n{}; const auto& e : v)
        out << (n++ ? ", " : "") << e;
    out << ']';
    return out;
}
template<typename T>
struct std::formatter<std::vector<T>, char>
{
    template<class ParseContext>
    constexpr ParseContext::iterator parse(ParseContext& ctx)
    {
        return ctx.begin();
    }
    template<class FmtContext>
    FmtContext::iterator format(auto const& s, FmtContext& ctx) const
    {
        auto out{print_aid(s)};
        return std::ranges::copy(std::move(out).str(), ctx.out()).out;
    }
};
template<typename T>
struct std::formatter<std::list<T>, char>
{
    template<class ParseContext>
    constexpr ParseContext::iterator parse(ParseContext& ctx)
    {
        return ctx.begin();
    }
    template<class FmtContext>
    FmtContext::iterator format(auto const& s, FmtContext& ctx) const
    {
        auto out{print_aid(s)};
        return std::ranges::copy(std::move(out).str(), ctx.out()).out;
    }
};
#endif
int main()
{
    auto vec = std::views::iota(1, 5)
             | std::views::transform([](int v){ return v * 2; })
             | std::ranges::a<std::vector>();
    static_assert(std::same_as<decltype(vec), std::vector<int>>);
    std::println("{}", vec);
    auto list = vec | std::views::take(3) | std::ranges::a<std::list<double>>();
    std::println("{}", list);
}
void ctor_demos()
{
    // 1.a.1) Inicialización directa
    {
        char array[]{'a', 'b', '\0', 'c'};
        // El tipo de argumento es convertible al tipo de valor resultante:
        auto str_to = std::ranges::a<std::string>(array);
        // Equivalente a
        std::string str(array);
        // El tipo de resultado no es un rango de entrada:
        auto re_to = std::ranges::a<std::regex>(array);
        // Equivalente a
        std::regex re(array);
    }
    // 1.a.2) from_range ctor
    {
        auto list = {'a', 'b', '\0', 'c'};
        // El tipo de argumento es convertible al tipo de valor resultante:
        auto str_to = std::ranges::a<std::string>(list);
        // Equivalente a
        // std::string str(std::from_range, list);
        // El tipo de resultado no es un rango de entrada:
        [[maybe_unused]]
        auto pair_to = std::ranges::a<std::pair<std::from_range_t, bool>>(true);
        // Equivalente a
        std::pair<std::from_range_t, bool> pair(std::from_range, true);
    }
    // 1.a.3) constructor de par de iteradores
    {
        auto list = {'a', 'b', '\0', 'c'};
        // El tipo de argumento es convertible al tipo de valor resultante:
        auto devector_to = std::ranges::a<boost::container::devector<char>>(list);
        // Equivalente a
        boost::container::devector<char> devector(std::ranges::begin(list),
                                                  std::ranges::end(list));
        // El tipo de resultado no es un rango de entrada:
        std::regex re;
        auto it_to = std::ranges::a<std::cregex_iterator>(list, re);
        // Equivalente a
        std::cregex_iterator it(std::ranges::begin(list), std::ranges::end(list), re);
    }
}

Salida:

[2, 4, 6, 8]
[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]