Compartir vía


Clases de vista <ranges>

Una vista es un intervalo ligero que hace referencia a elementos que no posee (excepto owning_view). Normalmente, una vista se basa en otro intervalo y proporciona una manera diferente de examinarla, ya sea mediante la transformación o el filtrado. Por ejemplo, std::views::filter es una vista que usa los criterios que se especifican para seleccionar elementos de otro intervalo.

Cuando se accede a los elementos de una vista, se hace "diferir" para que el trabajo se realice solo cuando se obtiene un elemento. Esto permite combinar o componer vistas sin una penalización de rendimiento.

Por ejemplo, podría crear una vista que proporcione solo los elementos pares de un rango y, a continuación, transformarlos mediante la cuartura de ellos. El trabajo para realizar el filtrado y la transformación solo se realiza para los elementos a los que accede y solo cuando se accede a ellos.

Una vista se puede copiar, asignar y destruir en tiempo constante, independientemente del número de elementos que contiene. Esto se debe a que una vista no posee los elementos a los que hace referencia, por lo que no es necesario realizar una copia. Este es el motivo por el que puede crear vistas sin una penalización de rendimiento.

Normalmente, se crea una vista mediante un adaptador de rango. Los adaptadores de rango son la manera prevista de crear una vista, son más fáciles de usar que crear instancias de las clases de vista directamente y, a veces, son más eficaces que crear instancias de las clases de vista directamente. Las clases de vista se exponen directamente en caso de que necesite crear su propio tipo de vista personalizado basado en un tipo de vista existente.

Este es un breve ejemplo de cómo crear una vista de los cuadrados de los elementos divisibles por tres en un vector:

// requires /std:c++20 or later
#include <ranges>
#include <vector>
#include <iostream>

int main()
{
    std::vector<int> input =  {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    auto divisible_by_three = [](const int n) {return n % 3 == 0;};
    auto square = [](const int n) {return n * n;};

    auto x = input
             | std::views::filter(divisible_by_three)
             | std::views::transform(square);

    for (int i : x)
    {
        std::cout << i << ' '; // 0 9 36 81
    }
}
0 9 36 81

El uso de una vista después del intervalo en el que se basa se modifica puede provocar un comportamiento indefinido. Por ejemplo, no se debe reutilizar un reverse_view elemento basado en un vector si agrega o quita elementos del vector subyacente. Al modificar el vector subyacente, se invalida el iterador del end contenedor, incluida la copia del iterador que puede haber realizado la vista.

Dado que las vistas son baratas de crear, normalmente debe volver a crear una vista si modifica el intervalo subyacente. En el ejemplo siguiente se muestra cómo almacenar una canalización de vista en una variable para poder reutilizarla.

// requires /std:c++20, or later
#include <iostream>
#include <ranges>
#include <vector>
#include <list>
#include <string_view>
#include <algorithm>

template<typename rangeType>
void show(std::string_view msg, rangeType r)
{
    std::cout << msg;
    std::ranges::for_each(r,
        [](auto e)
        {
            std::cout << e << ' ';
        });
    std::cout << '\n';
}

int main()
{
    std::vector v{ 1, 2, 3, 4 };
    show("v: ", v);

    // You can save a view pipeline
    auto rev3 = std::views::take(3) | std::views::reverse;

    show("v | rev3: ", v | rev3); // 3 2 1

    v.insert(v.begin(), 0); // v = 0 1 2 3 4
    show("v: ", v);

    // Because modifying the vector invalidates its iterators, rebuild the view.
    // We are reusing the view pipeline we saved earlier
    show("v | rev3(v): ", rev3(v));
}
v: 1 2 3 4
v | rev3: 3 2 1
v: 0 1 2 3 4
v | rev3(v): 2 1 0

Las siguientes clases de vista se definen en el std::ranges espacio de nombres .

Ver Descripción
basic_istream_viewC++20 Vista de elementos sucesivos de un flujo de entrada. Las especializaciones incluyen istream_view y wistream_view.
common_viewC++20 Adapta una vista que tiene diferentes tipos de iterador/sentinel en una vista con los mismos tipos de iterador o sentinel.
drop_viewC++20 Creado a partir de otra vista, omitiendo los primeros count elementos.
drop_while_viewC++20 Creado a partir de otra vista, omitiendo los elementos iniciales siempre que se mantenga un predicado.
elements_viewC++20 Vista sobre el índice seleccionado en cada valor similar a la tupla de una colección. Por ejemplo, dado un intervalo de std::tuple<string, int> valores, cree una vista que consta de todos los string elementos de cada tupla.
empty_viewC++20 Vista sin elementos.
filter_viewC++20 Filtra los elementos de un intervalo que no coinciden con un predicado.
iota_viewC++20 Vista generada que contiene una secuencia de valores de incremento.
join_viewC++20 Combina todos los elementos de varios intervalos en una sola vista.
keys_viewC++20 Vista sobre el primer índice en cada valor similar a la tupla de una colección. Por ejemplo, dado un intervalo de std::tuple<string, int> valores, cree una vista que consta de los string elementos de cada tupla.
lazy_split_viewC++20 Divide una vista en subrangos en función de un delimitador.
owning_viewC++20 Toma posesión de los elementos de otro intervalo.
ref_viewC++20 Vista que hace referencia a los elementos que pertenecen a otro intervalo.
reverse_viewC++20 Presenta los elementos de un intervalo en orden inverso.
single_viewC++20 Vista que contiene solo un elemento.
split_viewC++20 Divide una vista en subrangos en función de un delimitador.
subrangeC++20 Vista de parte de los elementos de un intervalo, tal como se define mediante un iterador inicial y un sentinel.
take_viewC++20 Contiene el número especificado de elementos tomados desde el principio de un intervalo.
take_while_viewC++20 Contiene los elementos iniciales de un intervalo que coinciden con el predicado especificado.
transform_viewC++20 Vista de una secuencia subyacente después de aplicar una función de transformación a cada elemento.
values_viewC++20 Vista sobre el segundo índice en cada valor similar a la tupla de una colección. Por ejemplo, dado un intervalo de std::tuple<string, int> valores, cree una vista que consta de los int elementos de cada tupla.

Muchas de estas clases tienen adaptadores de intervalo correspondientes en el std:views espacio de nombres que crea instancias de ellas. Prefiere usar un adaptador para crear una vista en lugar de crear clases de vista directamente. Los adaptadores de rango son la manera prevista de crear vistas, son más fáciles de usar y, en algunos casos, son más eficaces.

Ver las características de las clases

Cada tema de clase de vista tiene una sección Características después de la sección de sintaxis. La sección Características tiene las siguientes entradas:

  • Adaptador de rango: vínculo al adaptador de rango que crea la vista. Normalmente, se usa un adaptador de rango para crear una vista en lugar de crear una clase de vista directamente, por lo que se muestra aquí para mayor comodidad.

  • Intervalo subyacente: las vistas tienen diferentes requisitos de iterador para el tipo de intervalo subyacente que pueden usar. Consulte jerarquía de iteradores de rangos para obtener más información sobre los tipos de iteradores.

  • Ver categoría de iterador: la categoría iterador de la vista. Cuando una vista adapta un intervalo, el tipo de iterador de la vista suele ser el mismo que el tipo de iterador del intervalo subyacente. Sin embargo, puede ser diferente para algunas vistas. Por ejemplo, reverse_view tiene un bidirectional_iterator, incluso si el intervalo subyacente tiene un random_access_iterator.

  • Tipo de elemento: tipo de los elementos que devuelve el iterador de la vista.

  • Tamaño: indica si la vista puede devolver el número de elementos a los que hace referencia. No todas las vistas pueden.

  • Intervalo común: especifica si la vista es , common_rangelo que significa que los tipos de iterador inicial y centinela son los mismos. Los intervalos comunes son útiles para el código de intervalo previo que funciona con pares de iteradores. Un ejemplo es constructores de par de iteradores para un contenedor de secuencia, como vector(ranges::begin(x), ranges::end(x)).

  • Intervalo prestado: especifica si la vista es un intervalo prestado. borrowed_range<T> significa que puede usar iteradores para T después T de destruirse.

    Ningún contenedor estándar es un intervalo prestado, ya que al destruir el contenedor se liberan los elementos y se invalidan los iteradores. En ese caso, decimos que los iteradores se dejan "pendientes" después de la destrucción.

    Por ejemplo, std::ranges::find() normalmente devuelve un iterador al elemento encontrado en el argumento range. Si el argumento range es un contenedor temporal (rvalue), es un error almacenar el iterador devuelto y usarlo más adelante porque es "pendiente".

    Los algoritmos de intervalo que devuelven iteradores (o subrangos) solo lo hacen cuando sus argumentos son valores lvalues (no temporalmente) o intervalos prestados. De lo contrario, devuelven un std::dangling objeto , que proporciona una sugerencia en los mensajes de error sobre lo que salió mal si intentó usarlo como un iterador.

  • Es const iterable: indica si puede iterar en una const instancia de la vista. No todas las const vistas se pueden iterar. Si una vista no const se puede iterar, no se puede iterar con for (const auto& element : as_const(theView)) ni pasarla a una función que tome una const referencia a la vista e intente iterar sobre ella.

Jerarquía de iteradores de intervalos

En la sección Características de cada tema de clase de vista, la categoría iterador de la categoría Intervalo subyacente y Iterador vista hace referencia al tipo de iterador que admite el intervalo o la vista. Hay seis categorías de iteradores ranges, que se identifican mediante conceptos de C++20. La jerarquía de iteradores de intervalo, en orden creciente de funcionalidad, es:

Concepto de iterador de rango Descripción
output_range Solo escritura, solo avanza; paso único.
input_range Solo lectura, solo avanza; paso único.
forward_range Sólo avanza; multi-pass.
bidirectional_range Puede avanzar y retroceder; multi-pass.
random_access_range Puede acceder a la colección con un índice; multi-pass.
contiguous_range Puede acceder a la colección con un índice y los elementos se almacenan de forma contigua en la memoria.

Por lo general, un iterador tiene la capacidad de los iteradores que lo preceden en la tabla. Por ejemplo, bidirectional_range tiene las funcionalidades de forward_range, pero no viceversa. Excepto input_range, que no tiene la funcionalidad de output_range porque no se puede escribir en .input_range

La instrucción "requiere input_range o superior" significa que la vista se puede usar con un input_rangeiterador , forward_rangerandom_access_rangebidirectional_range, , o contiguous_range , porque todos son tan capaces como .input_range

La jerarquía de iteradores de intervalos está directamente relacionada con la jerarquía del iterador. Para obtener más información, consulte Conceptos de Iterador.

Consulte también

<ranges>
Adaptadores de rango