Partager via


<ranges>

À un niveau élevé, une plage est quelque chose que vous pouvez itérer. Une plage est représentée par un itérateur qui marque le début de la plage et une sentinelle qui marque la fin de la plage. Le sentinelle peut être le même type que l’itérateur de début, ou il peut être différent. Les conteneurs, tels que vector et list, dans la bibliothèque standard C++ sont des plages. Une plage extrait les itérateurs d’une manière qui simplifie et agrandit votre capacité à utiliser la bibliothèque de modèles standard (STL).

Les algorithmes STL prennent généralement des itérateurs qui pointent vers la partie de la collection sur laquelle ils doivent fonctionner. Par exemple, réfléchissez à la façon dont vous triez un vector à l’aide std::sort()de . Vous passez deux itérateurs qui marquent le début et la vectorfin du . Cela offre une flexibilité, mais le passage des itérateurs à l’algorithme est un travail supplémentaire, car vous voulez probablement simplement trier l’ensemble de la chose.

Avec des plages, vous pouvez appeler std::ranges::sort(myVector);, qui est traité comme si vous avez appelé std::sort(myVector.begin(), myVector.end());. Dans les bibliothèques de plages, les algorithmes prennent des plages en tant que paramètres (bien qu’ils puissent également prendre des itérateurs, si vous le souhaitez). Ils peuvent fonctionner directement sur des collections. Exemples d’algorithmes de plage disponibles dans <algorithm> include copy, copy_ifall_ofcopy_nany_ofnone_of, find, find_if, find_if_not, , count_ifcount, , for_each_nfor_each, , equal, et .mismatch

Mais peut-être l’avantage le plus important des plages est que vous pouvez composer des algorithmes STL qui fonctionnent sur des plages dans un style qui rappelle la programmation fonctionnelle.

Exemple de plages

Avant les plages, si vous souhaitez transformer les éléments d’une collection qui répondent à un certain critère, vous devez introduire une étape intermédiaire pour contenir les résultats entre les opérations. Par exemple, si vous souhaitez générer un vecteur de carrés à partir des éléments d’un autre vecteur qui sont divisibles par trois, vous pouvez écrire quelque chose comme suit :

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; });

Avec des plages, vous pouvez accomplir la même chose sans avoir besoin du intermediate vecteur :

// 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; });

En plus d’être plus facile à lire, ce code évite l’allocation de mémoire requise pour le intermediate vecteur et son contenu. Il vous permet également de composer deux opérations.

Dans le code précédent, chaque élément qui est divisible par trois est combiné avec une opération pour mettre en carré cet élément. Le symbole de canal (|) chaîne les opérations ensemble et est lu de gauche à droite.

Le résultat, outputest lui-même un type de plage appelé vue.

Vues

Une vue est une plage légère. Affichez les opérations, telles que la construction par défaut, déplacer la construction/affectation, copier la construction/affectation (le cas échéant), la destruction, le début et la fin- tout se produit dans le temps constant, quel que soit le nombre d’éléments de la vue.

Les vues sont créées par des adaptateurs de plage, qui sont abordés dans la section suivante. Pour plus d’informations sur les classes qui implémentent différentes vues, consultez Afficher les classes.

La façon dont les éléments de la vue apparaissent dépend de l’adaptateur de plage que vous utilisez pour créer la vue. Dans l’exemple précédent, un adaptateur de plage prend une plage et retourne une vue des éléments divisent par trois. La plage sous-jacente est inchangée.

Les vues sont composables, ce qui est puissant. Dans l’exemple précédent, la vue des éléments vectoriels qui sont divisibles par trois est combinée à la vue qui place ces éléments.

Les éléments d’une vue sont évalués de manière différée. Autrement dit, les transformations que vous appliquez à chaque élément d’une vue ne sont pas évaluées tant que vous ne demandez pas l’élément. Par exemple, si vous exécutez le code suivant dans un débogueur et placez un point d’arrêt sur les lignes auto divisible_by_three = ... et auto square = ...que vous verrez que vous atteignez le divisible_by_three point d’arrêt lambda, car chaque élément dans input lequel il est testé pour la divisibilité par trois. Le square point d’arrêt lambda est atteint, car les éléments qui sont divisibles par trois sont carrés.

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

Pour plus d’informations sur les vues, consultez <ranges> les classes d’affichage.

Adaptateurs de plage

Les adaptateurs de plage prennent une plage et produisent une vue. Les adaptateurs de plage produisent des vues évaluées de manière différée. Autrement dit, vous n’entraînez pas le coût de transformation de chaque élément de la plage pour produire la vue. Vous payez uniquement le coût de traitement d’un élément dans la vue lorsque vous accédez à cet élément.

Dans l’exemple précédent, l’adaptateur filter de plage crée une vue nommée input qui contient les éléments qui sont divisent par trois. L’adaptateur transform de plage prend la vue des éléments divisés par trois et crée une vue de ces éléments carrés.

Les adaptateurs de plage peuvent être chaînés (composés), qui est le cœur de la puissance et de la flexibilité des plages. La composition d’adaptateurs de plage vous permet de surmonter le problème que les algorithmes STL précédents ne sont pas facilement composables.

Pour plus d’informations sur la création de vues, consultez les adaptateurs de plage.

Algorithmes de plage

Certains algorithmes de plage prennent un argument de plage. par exemple std::ranges::sort(myVector);.

Les algorithmes de plage sont presque identiques aux algorithmes itérateurs-paires correspondants dans l’espace std de noms. La différence est qu’ils ont des contraintes appliquées au concept et qu’ils acceptent des arguments de plage ou plusieurs paires d’arguments itérateur-sentinel. Ils peuvent travailler directement sur un conteneur et peuvent être facilement chaînés ensemble.

fonctions<ranges>

Les fonctions suivantes permettent de créer des itérateurs et des sentinelles pour les plages et d’obtenir la taille d’une plage.

Fonction Description
beginC++20 Obtenez un itérateur sur le premier élément de la plage.
cbeginC++20 Obtenez un const itérateur au premier élément de la plage.
cendC++20 Obtenez la sentinelle à la fin de la constplage qualifiée.
cdataC++20 Obtenez un const pointeur vers le premier élément de la plage contiguë.
crbeginC++20 Obtenez un itérateur inverse const au début de la plage.
crendC++20 Obtenez la sentinelle à la fin de ce qui crbegin() retourne.
dataC++20 Obtenez un pointeur vers le premier élément de la plage contiguë.
emptyC++20 Déterminez si la plage est vide.
endC++20 Obtenez la sentinelle à la fin de la plage.
rbeginC++20 Obtenez un itérateur inverse au début de la plage.
rendC++20 Obtenez un itérateur inverse vers la sentinelle à la fin de la plage.
sizeC++20 Obtenez la taille de la plage en tant que valeur non signée.
ssizeC++20 Obtenez la taille de la plage en tant que valeur signée.

Pour plus d’informations, consultez <ranges> les fonctions.

Concepts de plage

La façon dont vous effectuez une itération sur les éléments d’une plage dépend de son type d’itérateur sous-jacent. Les plages utilisent des concepts C++ qui spécifient l’itérateur qu’ils prennent en charge.

En C++20, pour dire que le concept X affine le concept Y signifie que tout ce qui satisfait le concept Y satisfait également le concept X. Par exemple : voiture, bus et camions, tous les véhicules raffinés.

Certains concepts de plage reflètent la hiérarchie des catégories d’itérateurs. Le tableau suivant répertorie les concepts de plage, ainsi que les types de conteneurs auxquels ils peuvent être appliqués.

Concept de plage Description Conteneurs pris en charge
std::ranges::output_range Peut itérer vers l’avant.
std::ranges::input_range Peut itérer de début à fin au moins une fois. std::forward_list
std::unordered_map
std::unordered_multimap
std::unordered_set
std::unordered_multiset
basic_istream_view
std::ranges::forward_range Peut itérer de début à fin plusieurs fois. std::forward_list
std::unordered_map
std::unordered_multimap
std::unordered_set
std::unordered_multiset
std::ranges::bidirectional_range Peut itérer plus d’une fois vers l’avant et vers l’arrière. std::list
std::map
std::multimap
std::multiset
std::set
std::ranges::random_access_range Peut accéder à un élément arbitraire (en temps constant) à l’aide de l’opérateur [] . std::deque
std::ranges::contiguous_range Les éléments sont stockés en mémoire consécutivement. std::array
std::string
std::vector

Pour plus d’informations sur ces concepts, consultez les <ranges> concepts ci-après.

<ranges> modèles d’alias

Les modèles d’alias suivants déterminent les types d’itérateurs et de sentinelles d’une plage :

Modèle d’alias Description
borrowed_iterator_tC++20 Déterminez si un itérateur retourné pour une range plage fait référence à une plage dont la durée de vie a pris fin.
borrowed_subrange_tC++20 Déterminez si un itérateur retourné pour une subrange référence à une sous-plage dont la durée de vie a pris fin.
danglingC++20 Indique que l’itérateur retourné d’une durée de vie de celle-ci/subrange rangefait référence.range/subrange
iterator_tC++20 Retourne le type d’itérateur du type de plage spécifié.
range_difference_tC++20 Retourne le type de différence du type d’itérateur de la plage spécifiée.
range_reference_tC++20 Retourne le type de référence du type d’itérateur de la plage spécifiée.
range_rvalue_reference_tC++20 Retourne le type de référence rvalue pour le type d’itérateur de la plage spécifiée. En d’autres termes, le type de référence rvalue des éléments de la plage.
range_size_tC++20 Retourne le type utilisé pour signaler la taille de la plage spécifiée.
range_value_tC++20 Retourne le type valeur du type d’itérateur de la plage spécifiée. En d’autres termes, le type des éléments de la plage.
sentinel_tC++20 Retourne le type sentinelle de la plage spécifiée.

Pour plus d’informations sur ces modèles d’alias, consultez <ranges> les modèles d’alias.

Voir aussi

<ranges>, fonctions
<ranges> Concepts
Adaptateurs de plage
Informations de référence sur les fichiers d’en-tête