Partilhar via


<ranges> ver aulas

Uma vista é um intervalo leve que se refere a elementos que não lhe pertencem (exceto owning_view). Normalmente, uma vista baseia-se noutro intervalo e fornece uma forma diferente de o ver, quer o transforme quer filtre. Por exemplo, std::views::filter é um modo de exibição que usa os critérios especificados para selecionar elementos de outro intervalo.

Quando você acessa os elementos em uma exibição, isso é feito "preguiçosamente" para que o trabalho seja feito apenas quando você obtém um elemento. Isso torna possível combinar, ou compor, pontos de vista sem uma penalidade de desempenho.

Por exemplo, você pode criar um modo de exibição que forneça apenas os elementos pares de um intervalo e, em seguida, transformá-los em quadratura. O trabalho para fazer a filtragem e a transformação é feito apenas para os elementos que você acessa e somente quando você os acessa.

Uma vista pode ser copiada, atribuída e destruída em tempo constante, independentemente do número de elementos que contenha. Isso ocorre porque um modo de exibição não possui os elementos aos quais se refere, portanto, não precisa fazer uma cópia. É por isso que você pode compor visualizações sem uma penalidade de desempenho.

Normalmente, você cria uma exibição usando um adaptador de intervalo. Os adaptadores de intervalo são a maneira pretendida de criar uma exibição, são mais fáceis de usar do que instanciar as classes de exibição diretamente e, às vezes, são mais eficientes do que instanciar as classes de exibição diretamente. As classes de exibição são expostas diretamente caso você precise criar seu próprio tipo de exibição personalizado com base em um tipo de exibição existente.

Aqui está um breve exemplo de criação de uma visão dos quadrados dos elementos que são divisíveis por três em um vetor:

// 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

Usar um modo de exibição depois que o intervalo no qual ele se baseia é modificado pode levar a um comportamento indefinido. Por exemplo, um reverse_view vetor baseado em um não deve ser reutilizado se você adicionar ou remover elementos do vetor subjacente. Modificar o vetor subjacente invalida o iterador do end contêiner - incluindo a cópia do iterador que a exibição pode ter feito.

Como os modos de exibição são baratos de criar, você geralmente deve recriar um modo de exibição se modificar o intervalo subjacente. O exemplo a seguir demonstra como armazenar um pipeline de exibição em uma variável para que você possa reutilizá-lo.

// 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

As classes de exibição a std::ranges seguir são definidas no namespace.

Visualizar Descrição
basic_istream_view C++20 Uma visão de elementos sucessivos de um fluxo de entrada. As especializações incluem istream_view e wistream_view.
common_view C++20 Adapta uma exibição que tem diferentes tipos de iterador/sentinela em uma exibição com os mesmos tipos de iterador/sentinela.
drop_view C++20 Criado a partir de outra vista, ignorando os primeiros count elementos.
drop_while_view C++20 Criado a partir de outra exibição, ignorando elementos à esquerda desde que um predicado seja mantido.
elements_view C++20 Uma exibição sobre o índice selecionado em cada valor semelhante a uma tupla em uma coleção. Por exemplo, dado um intervalo de std::tuple<string, int> valores, crie uma exibição que consiste em todos os string elementos de cada tupla.
empty_view C++20 Uma vista sem elementos.
filter_view C++20 Filtra elementos de um intervalo que não correspondem a um predicado.
iota_view C++20 Uma exibição gerada que contém uma sequência de valores de incremento.
join_view C++20 Combina todos os elementos de vários intervalos em uma única visualização.
keys_view C++20 Uma exibição sobre o primeiro índice em cada valor semelhante a uma tupla em uma coleção. Por exemplo, dado um intervalo de std::tuple<string, int> valores, crie uma exibição que consiste nos string elementos de cada tupla.
lazy_split_view C++20 Divide uma exibição em subintervalos com base em um delimitador.
owning_view C++20 Assume a propriedade dos elementos de outro intervalo.
ref_view C++20 Um modo de exibição que faz referência aos elementos que pertencem a outro intervalo.
reverse_view C++20 Apresenta os elementos de um intervalo em ordem inversa.
single_view C++20 Um modo de exibição que contém apenas um elemento.
split_view C++20 Divide uma exibição em subintervalos com base em um delimitador.
subrange C++20 Uma visão de parte dos elementos de um intervalo, conforme definido por um iterador inicial e uma sentinela.
take_view C++20 Contém o número especificado de elementos retirados da frente de um intervalo.
take_while_view C++20 Contém os elementos principais de um intervalo que correspondem ao predicado dado.
transform_view C++20 Uma exibição de uma sequência subjacente após uma função de transformação é aplicada a cada elemento.
values_view C++20 Uma exibição sobre o segundo índice em cada valor semelhante a uma tupla em uma coleção. Por exemplo, dado um intervalo de std::tuple<string, int> valores, crie uma exibição que consiste nos int elementos de cada tupla.

Muitas dessas classes têm adaptadores de intervalo correspondentes no std::views namespace que cria instâncias delas. Prefira usar um adaptador para criar um modo de exibição em vez de criar classes de exibição diretamente. Os adaptadores de gama são a forma pretendida de criar vistas, são mais fáceis de utilizar e, em alguns casos, são mais eficientes.

Ver características das classes

Cada tópico de classe de exibição tem uma seção Características após a seção de sintaxe. A seção Características tem as seguintes entradas:

  • Adaptador de intervalo: um link para o adaptador de intervalo que cria a exibição. Normalmente, você usa um adaptador de intervalo para criar um modo de exibição em vez de criar uma classe de exibição diretamente, por isso ele é listado aqui por conveniência.

  • Intervalo subjacente: as visualizações têm requisitos de iterador diferentes para o tipo de intervalo subjacente que podem usar. Consulte hierarquia de iteradores de intervalos para obter mais informações sobre os tipos de iteradores.

  • View iterator category: A categoria iterador da exibição. Quando um modo de exibição adapta um intervalo, o tipo de iterador para o modo de exibição é normalmente o mesmo que o tipo de iterador do intervalo subjacente. No entanto, pode ser diferente para alguns pontos de vista. Por exemplo, reverse_view tem um bidirectional_iterator, mesmo que o intervalo subjacente tenha um random_access_iterator.

  • Tipo de elemento: o tipo dos elementos que o iterador da exibição retorna.

  • Dimensionado: se o modo de exibição pode retornar o número de elementos aos quais se refere. Nem todas as visualizações podem.

  • Intervalo comum: especifica se a exibição é um common_range, o que significa que os tipos iterador inicial e sentinela são os mesmos. Intervalos comuns são úteis para código pré-intervalo que funciona com pares de iteradores. Um exemplo são construtores de par iterador para um contêiner de sequência, como vector(ranges::begin(x), ranges::end(x)).

  • Intervalo emprestado: especifica se o modo de exibição é um intervalo emprestado. borrowed_range<T> significa que você pode usar iteradores para T depois que T for destruído.

    Nenhum contêiner padrão é um intervalo emprestado, porque destruir o contêiner libera os elementos e invalida quaisquer iteradores. Nesse caso, dizemos que os iteradores são deixados "pendurados" após a destruição.

    Por exemplo, std::ranges::find() normalmente retorna um iterador para o elemento encontrado no argumento range. Se o argumento range for um contêiner temporário (rvalue), é um erro armazenar o iterador retornado e usá-lo mais tarde porque ele está "pendurado".

    Os algoritmos de intervalo que retornam iteradores (ou subintervalos) o fazem somente quando seus argumentos são lvalues (não temporários) ou intervalos emprestados. Caso contrário, eles retornam um std::dangling objeto, que fornece uma dica em mensagens de erro sobre o que deu errado se você tentou usá-lo como um iterador.

  • É const iterável: indica se você pode iterar em uma const instância do modo de exibição. Nem todas as const visualizações podem ser iteradas. Se um modo de exibição não const for iterável, você não poderá iterá-lo ou for (const auto& element : as_const(theView)) passá-lo para uma função que faça uma const referência ao modo de exibição e, em seguida, tente iterar sobre ele.

Hierarquia do iterador de intervalos

Na seção Características de cada tópico de classe de exibição, a categoria iterador em Intervalo subjacente e categoria de iterador de exibição refere-se ao tipo de iterador que o intervalo/exibição suporta. Existem seis categorias de iteradores de intervalos, que são identificados pelos conceitos C++20. A hierarquia dos iteradores de intervalo, em ordem crescente de capacidade, é:

Conceito de iterador de alcance Descrição
output_range Somente gravação, só avança; Passagem única.
input_range Somente leitura, só avança; Passagem única.
forward_range Só avança; multi-passagem.
bidirectional_range Pode avançar e retroceder; multi-passagem.
random_access_range Pode aceder à coleção com um índice; multi-passagem.
contiguous_range Pode acessar a coleção com um índice, e os elementos são armazenados contíguamente na memória.

De um modo geral, um iterador tem a capacidade dos iteradores que o precedem na tabela. Por exemplo, bidirectional_range tem as capacidades de forward_range, mas não vice-versa. Exceto input_range, que não tem a capacidade de output_range porque você não pode escrever para um input_range.

A instrução "requer input_range ou superior" significa que a exibição pode ser usada com um input_range, forward_range, bidirectional_range, random_access_range, ou contiguous_range iterador, porque todos eles são tão capazes quanto input_range.

A hierarquia do iterador de intervalos está diretamente relacionada à hierarquia do iterador. Para obter mais informações, consulte Conceitos de iterador.

Ver também

<ranges>
Adaptadores de gama