Classes de exibição de <ranges>
Um modo de exibição é um intervalo leve que se refere a elementos que não são de sua propriedade (exceto owning_view
). Uma exibição geralmente é baseada em outro intervalo e fornece uma maneira diferente de visualizá-la, seja transformando-a ou filtrando-a. Por exemplo, std::views::filter
é uma 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 somente quando você obtém um elemento. Isso possibilita combinar ou compor exibições sem uma penalidade de desempenho.
Por exemplo, você pode criar uma vista que forneça apenas os elementos pares de um intervalo e, em seguida, transformá-los ao quadrado. 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 visualização pode ser copiada, atribuída e destruída em tempo constante, independentemente de quantos elementos ela contenha. Isso ocorre porque uma 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 exibiçõ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 visualizaçã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 uma exibição após a modificação do intervalo em que ela se baseia pode levar a um comportamento indefinido. Por exemplo, um reverse_view
baseado em um vetor 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 as exibições são baratas de criar, geralmente você deve recriar uma 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 seguir são definidas no std::ranges
namespace.
Visualizar | Descrição |
---|---|
basic_istream_view C++20 |
Uma exibiçã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 visualização, ignorando os primeiros count elementos. |
drop_while_view C++20 |
Criado a partir de outra exibição, ignorando elementos principais enquanto um predicado for válido. |
elements_view C++20 |
Uma exibição sobre o índice selecionado em cada valor semelhante a tupla em uma coleção. Por exemplo, dado um intervalo de std::tuple<string, int> valores, crie uma exibição que consista 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 incrementais. |
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 tupla em uma coleção. Por exemplo, dado um intervalo de std::tuple<string, int> valores, crie uma exibição que consista 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 |
Apropria-se dos elementos de outro intervalo. |
ref_view C++20 |
Uma 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 |
Uma 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 exibição de parte dos elementos de um intervalo, conforme definido por um iterador begin e um 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 fornecido. |
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 tupla em uma coleção. Por exemplo, dado um intervalo de std::tuple<string, int> valores, crie uma exibição que consista 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 uma exibição em vez de criar classes de exibição diretamente. Os adaptadores de intervalo são a maneira pretendida de criar visualizações, são mais fáceis de usar e, em alguns casos, são mais eficientes.
Exibir 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 uma exibição em vez de criar uma classe de exibição diretamente, portanto, ele é listado aqui por conveniência.
Intervalo subjacente: as exibiçõ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.
Categoria do iterador de exibição: a categoria do iterador da exibição. Quando uma exibição adapta um intervalo, o tipo de iterador da 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 umbidirectional_iterator
, mesmo que o intervalo subjacente tenha umrandom_access_iterator
.Tipo de elemento: o tipo dos elementos que o iterador da exibição retorna.
Dimensionado: se a exibição pode retornar o número de elementos aos quais ela se refere. Nem todas as visualizações podem.
Intervalo comum: especifica se a exibição é um
common_range
, o que significa que os tipos de iterador inicial e sentinela são os mesmos. Os intervalos comuns são úteis para código pré-intervalo que funciona com pares de iteradores. Um exemplo são os construtores de pares de iteradores para um contêiner de sequência, comovector(ranges::begin(x), ranges::end(x))
.Intervalo emprestado: especifica se a exibição é um intervalo emprestado.
borrowed_range<T>
significa que você pode usar iteradores paraT
depoisT
de ser destruído.Nenhum contêiner padrão é um intervalo emprestado, pois destruir o contêiner libera os elementos e invalida todos os 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), será um erro armazenar o iterador retornado e usá-lo mais tarde porque ele está "pendente".Os algoritmos de intervalo que retornam iteradores (ou subintervalos) fazem isso 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ê tentar usá-lo como um iterador.É
const
iterável: indica se você pode iterar em umaconst
instância da exibição. Nem todas asconst
visualizações podem ser iteradas. Se uma exibição nãoconst
for iterável, você não poderá iterar oufor (const auto& element : as_const(theView))
passá-la para uma função que usa umaconst
referência à exibição e tenta iterar sobre ela.
Hierarquia do iterador de intervalos
Na seção Características de cada tópico de classe de exibição, a categoria do iterador em Intervalo subjacente e Categoria do iterador de exibição refere-se ao tipo de iterador compatível com o intervalo/exibição. Há seis categorias de iteradores Ranges, que são identificadas por conceitos do C++20. A hierarquia de iteradores de intervalo, em ordem crescente de capacidade, é:
Conceito de iterador de intervalo | Descrição |
---|---|
output_range |
Somente gravação, apenas avança; passagem única. |
input_range |
Somente leitura, apenas avança; passagem única. |
forward_range |
Só avança; multi-passagem. |
bidirectional_range |
Pode se mover para frente e para trás; multi-passagem. |
random_access_range |
Pode acessar a coleção com um índice; multi-passagem. |
contiguous_range |
Pode acessar a coleção com um índice e os elementos são armazenados de forma contígua 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 gravar em um input_range
.
A instrução "requer input_range
ou superior" significa que a exibição pode ser usada com um input_range
iterador , forward_range
, bidirectional_range
, random_access_range
, ou contiguous_range
, 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 do iterador.