<ranges>
Em um alto nível, um intervalo é algo que você pode iterar. Um intervalo é representado por um iterador que marca o início do intervalo e uma sentinela que marca o fim do intervalo. O sentinela pode ser do mesmo tipo que o iterador begin ou pode ser diferente. Os contêineres, como vector
e list
, na Biblioteca Padrão do C++ são intervalos. Um intervalo abstrai iteradores de uma maneira que simplifica e amplifica sua capacidade de usar a STL (Biblioteca de Modelos Padrão).
Os algoritmos STL geralmente usam iteradores que apontam para a parte da coleção na qual eles devem operar. Por exemplo, considere como você classifica um vector
usando std::sort()
. Você passa dois iteradores que marcam o início e o fim do vector
. Isso fornece flexibilidade, mas passar os iteradores para o algoritmo é um trabalho extra porque você provavelmente só quer classificar a coisa toda.
Com intervalos, você pode chamar std::ranges::sort(myVector);
, que é tratado como se você tivesse chamado std::sort(myVector.begin(), myVector.end());
. Nas bibliotecas de intervalos, os algoritmos usam intervalos como parâmetros (embora também possam usar iteradores, se você quiser). Eles podem operar diretamente nas coleções. Exemplos de algoritmos de intervalo disponíveis incluem <algorithm>
, , , for_each
count
equal
find
find_if
find_if_not
for_each_n
mismatch
any_of
count_if
all_of
none_of
copy_if
copy_n
copy
Mas talvez o benefício mais importante dos intervalos seja que você pode compor algoritmos STL que operam em intervalos em um estilo que lembra a programação funcional.
Um exemplo de intervalos
Antes dos intervalos, se você quisesse transformar os elementos de uma coleção que atendesse a um determinado critério, seria necessário introduzir uma etapa intermediária para manter os resultados entre as operações. Por exemplo, se você quisesse construir um vetor de quadrados a partir dos elementos em outro vetor que são divisíveis por três, você poderia escrever algo como:
std::vector<int> input = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
std::vector<int> intermediate, output;
std::copy_if(input.begin(), input.end(), std::back_inserter(intermediate), [](const int i) { return i%3 == 0; });
std::transform(intermediate.begin(), intermediate.end(), std::back_inserter(output), [](const int i) {return i*i; });
Com intervalos, você pode realizar a mesma coisa sem precisar do vetor intermediate
:
// requires /std:c++20
std::vector<int> input = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto output = input
| std::views::filter([](const int n) {return n % 3 == 0; })
| std::views::transform([](const int n) {return n * n; });
Além de ser mais fácil de ler, esse código evita a alocação de memória necessária para o intermediate
vetor e seu conteúdo. Também permite compor duas operações.
No código anterior, cada elemento divisível por três é combinado com uma operação para elevar esse elemento ao quadrado. O símbolo de barra vertical (|
) encadeia as operações e é lido da esquerda para a direita.
O resultado, output
, é em si um tipo de intervalo chamado de visão.
Exibições
Uma exibição é um intervalo leve. As operações de visualização, como construção padrão, movimentação/atribuição, cópia, construção/atribuição (se presente), destruição, início e fim, ocorrem em tempo constante, independentemente do número de elementos na vista.
As exibições são criadas por adaptadores de intervalo, que são discutidos na seção a seguir. Para obter mais informações sobre as classes que implementam várias exibições, consulte Exibir classes.
A aparência dos elementos no modo de exibição depende do adaptador de intervalo usado para criar o modo de exibição. No exemplo anterior, um adaptador de intervalo usa um intervalo e retorna uma exibição dos elementos divisíveis por três. O intervalo subjacente é inalterado.
As visualizações são combináveis, o que é poderoso. No exemplo anterior, a exibição de elementos vetoriais divisíveis por três é combinada com a exibição que eleva esses elementos ao quadrado.
Os elementos de uma exibição não são avaliados automaticamente. Ou seja, as transformações que você aplica a cada elemento em uma exibição não são avaliadas até que você solicite o elemento. Por exemplo, se você executar o código a seguir em um depurador e colocar um ponto de interrupção nas linhas auto divisible_by_three = ...
e auto square = ...
, você verá que atingiu o ponto de interrupção lambda divisible_by_three
à medida que cada elemento em input
é testado para divisibilidade por três. O ponto de interrupção lambda square
será alcançado conforme os elementos divisíveis por três forem elevados ao quadrado.
// requires /std:c++20
#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 << '\n';
}
return 0;
}
Para obter mais informações sobre exibições, consulte <ranges>
classes de exibição.
Adaptadores de intervalo
Os adaptadores de alcance pegam um intervalo e produzem uma visualização. Adaptadores de intervalo produzem exibições avaliadas de maneira não automática. Ou seja, você não incorre no custo de transformar cada elemento no intervalo para produzir a exibição. Você só paga o custo para processar um elemento na exibição quando acessa esse elemento.
No exemplo anterior, o adaptador de filter
intervalo cria uma exibição chamada input
que contém os elementos que são divisíveis por três. O transform
adaptador de intervalo usa a exibição dos elementos divisíveis por três e cria uma exibição desses elementos ao quadrado.
Os adaptadores de alcance podem ser encadeados (compostos), o que é o coração do poder e da flexibilidade dos intervalos. A composição de adaptadores de intervalo permite que você supere o problema de que os algoritmos STL anteriores não são facilmente combináveis.
Para obter mais informações sobre como criar exibições, consulte Adaptadores de intervalo.
Algoritmos de intervalo
Alguns algoritmos de intervalo usam um argumento de intervalo. Um exemplo é std::ranges::sort(myVector);
.
Os algoritmos de intervalo são quase idênticos aos algoritmos de par de iteradores correspondentes no std
namespace. A diferença é que eles têm restrições impostas por conceito e aceitam argumentos de intervalo ou mais pares de argumentos iterador-sentinela. Eles podem trabalhar diretamente em um contêiner e podem ser facilmente encadeados.
Funções <ranges>
As funções a seguir são usadas para criar iteradores e sentinelas para intervalos e para obter o tamanho de um intervalo.
Função | Descrição |
---|---|
begin C++20 |
Obtenha um iterador para o primeiro elemento no intervalo. |
cbegin C++20 |
Obtenha um iterador const para o primeiro elemento no intervalo. |
cend C++20 |
Obtenha o sentinela no final do intervalo qualificado por const . |
cdata C++20 |
Obtenha um ponteiro const para o primeiro elemento no intervalo contíguo. |
crbegin C++20 |
Obtenha um iterador reverso const para o início do intervalo. |
crend C++20 |
Obtenha o sentinela no final do que crbegin() retorna. |
data C++20 |
Obtenha um ponteiro para o primeiro elemento no intervalo contíguo. |
empty C++20 |
Determine se o intervalo está vazio. |
end C++20 |
Obtenha o sentinela no final do intervalo. |
rbegin C++20 |
Obtenha um iterador reverso para o início do intervalo. |
rend C++20 |
Obtenha um iterador reverso para o sentinela no final do intervalo. |
size C++20 |
Obtenha o tamanho do intervalo como um valor sem sinal. |
ssize C++20 |
Obtenha o tamanho do intervalo como um valor com sinal. |
Para obter mais informações, consulte <ranges>
funções.
Conceitos de alcance
A forma como você itera sobre os elementos de um intervalo depende de seu tipo de iterador subjacente. Os intervalos usam conceitos C++ que especificam a qual iterador eles dão suporte.
No C++20, dizer que o conceito X refina o conceito Y significa que tudo o que satisfaz o conceito Y também satisfaz o conceito X. Por exemplo: carro, ônibus e caminhão refinam todos os veículos.
Alguns conceitos de intervalo refletem a hierarquia das categorias do iterador. A tabela a seguir lista os conceitos de intervalo, juntamente com os tipos de contêineres aos quais eles podem ser aplicados.
Conceito de intervalo | Descrição | Contêineres compatíveis |
---|---|---|
std::ranges::output_range |
Pode iterar para frente. | |
std::ranges::input_range |
Pode iterar do início ao fim pelo menos uma vez. | std::forward_list std::unordered_map std::unordered_multimap std::unordered_set std::unordered_multiset basic_istream_view |
std::ranges::forward_range |
Pode iterar do início ao fim mais de uma vez. | std::forward_list std::unordered_map std::unordered_multimap std::unordered_set std::unordered_multiset |
std::ranges::bidirectional_range |
Pode iterar para frente e para trás mais de uma vez. | std::list std::map std::multimap std::multiset std::set |
std::ranges::random_access_range |
Pode acessar um elemento arbitrário (em tempo constante) usando o [] operador. |
std::deque |
std::ranges::contiguous_range |
Os elementos são armazenados na memória consecutivamente. | std::array std::string std::vector |
Consulte <ranges>
conceitos para obter mais informações sobre esses conceitos.
Modelos de alias de <ranges>
Os seguintes modelos de alias determinam os tipos de iteradores e sentinelas para um intervalo:
Modelo de alias | Descrição |
---|---|
borrowed_iterator_t C++20 |
Determine se um iterador retornado para a range se refere a um intervalo cujo tempo de vida terminou. |
borrowed_subrange_t C++20 |
Determine se um iterador retornado para a subrange se refere a um subintervalo cujo tempo de vida terminou. |
dangling C++20 |
Indica que o iterador retornado de um range /subrange sobrevive ao tempo de vida do qual ele range /subrange se refere. |
iterator_t C++20 |
Retorna o tipo de iterador do tipo de intervalo especificado. |
range_difference_t C++20 |
Retorna o tipo de diferença do tipo de iterador do intervalo especificado. |
range_reference_t C++20 |
Retorna o tipo de referência do tipo de iterador do intervalo especificado. |
range_rvalue_reference_t C++20 |
Retorna o tipo de referência rvalue para o tipo de iterador do intervalo especificado. Em outras palavras, o tipo de referência rvalue dos elementos do intervalo. |
range_size_t C++20 |
Retorna o tipo usado para relatar o tamanho do intervalo especificado. |
range_value_t C++20 |
Retorna o tipo de valor do tipo de iterador do intervalo especificado. Ou, em outras palavras, o tipo dos elementos no intervalo. |
sentinel_t C++20 |
Retorna o tipo sentinela do intervalo especificado. |
Para obter mais informações sobre esses modelos de alias, consulte <ranges>
modelos de alias.
Confira também
Funções <ranges>
<ranges>
Conceitos
Adaptadores de gama
Referência de arquivos de cabeçalho