Partager via


<ranges> classes de vues

Une vue est une plage légère qui fait référence aux éléments qu’elle ne possède pas (sauf owning_view). Une vue est généralement basée sur une autre plage et fournit un autre moyen de l’examiner, qu’il s’agisse de la transformer ou de la filtrer. Par exemple, std::views::filter est une vue qui utilise les critères que vous spécifiez pour sélectionner des éléments d’une autre plage.

Lorsque vous accédez aux éléments d’une vue, il est effectué de manière « différée » afin que le travail soit effectué uniquement lorsque vous obtenez un élément. Cela permet de combiner ou de composer des vues sans pénalité de performance.

Par exemple, vous pouvez créer une vue qui fournit uniquement les éléments pairs d’une plage, puis les transformer en les mettant à l’écart. Le travail de filtrage et de transformation est effectué uniquement pour les éléments auxquels vous accédez, et uniquement lorsque vous y accédez.

Une vue peut être copiée, affectée et détruite en temps constant, quel que soit le nombre d’éléments qu’il contient. Cela est dû au fait qu’une vue ne possède pas les éléments auxquels il fait référence. Il n’est donc pas nécessaire d’effectuer une copie. C’est pourquoi vous pouvez composer des vues sans pénalité de performance.

Vous créez généralement une vue à l’aide d’un adaptateur de plage. Les adaptateurs de plage sont la façon prévue de créer un affichage, sont plus faciles à utiliser que d’instancier directement les classes d’affichage et sont parfois plus efficaces que d’instancier directement les classes d’affichage. Les classes d’affichage sont exposées directement si vous devez créer votre propre type d’affichage personnalisé en fonction d’un type d’affichage existant.

Voici un bref exemple de création d’une vue des carrés des éléments qui sont divisibles par trois dans un vecteur :

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

L’utilisation d’une vue après la plage sur laquelle elle est basée sur est modifiée peut entraîner un comportement non défini. Par exemple, un reverse_view vecteur basé sur un vecteur ne doit pas être réutilisé si vous ajoutez ou supprimez des éléments du vecteur sous-jacent. La modification du vecteur sous-jacent invalide l’itérateur du end conteneur, y compris la copie de l’itérateur que la vue a pu effectuer.

Étant donné que les vues sont bon marché pour créer, vous devez généralement recréer une vue si vous modifiez la plage sous-jacente. L’exemple suivant montre comment stocker un pipeline d’affichage dans une variable afin de pouvoir le réutiliser.

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

Les classes d’affichage suivantes sont définies dans l’espace std::ranges de noms.

Vue Description
basic_istream_viewC++20 Vue des éléments successifs d’un flux d’entrée. Les spécialisations incluent istream_view et wistream_view.
common_viewC++20 Adapte une vue qui a différents types itérateur/sentinel dans une vue avec les mêmes types itérateur/sentinelle.
drop_viewC++20 Créé à partir d’un autre affichage, ignorez les premiers count éléments.
drop_while_viewC++20 Créé à partir d’une autre vue, ignorez les éléments de début tant qu’un prédicat contient.
elements_viewC++20 Vue sur l’index sélectionné dans chaque valeur de type tuple dans une collection. Par exemple, en fonction d’une plage de std::tuple<string, int> valeurs, créez une vue qui se compose de tous les string éléments de chaque tuple.
empty_viewC++20 Vue sans éléments.
filter_viewC++20 Filtre les éléments d’une plage qui ne correspondent pas à un prédicat.
iota_viewC++20 Vue générée qui contient une séquence de valeurs d’incrémentation.
join_viewC++20 Combine tous les éléments de plusieurs plages en un seul affichage.
keys_viewC++20 Vue sur le premier index dans chaque valeur de type tuple dans une collection. Par exemple, en fonction d’une plage de std::tuple<string, int> valeurs, créez une vue qui se compose des string éléments de chaque tuple.
lazy_split_viewC++20 Fractionne une vue en sous-plages en fonction d’un délimiteur.
owning_viewC++20 Prend possession des éléments d’une autre plage.
ref_viewC++20 Vue qui référence les éléments appartenant à une autre plage.
reverse_viewC++20 Présente les éléments d’une plage dans l’ordre inverse.
single_viewC++20 Vue qui ne contient qu’un seul élément.
split_viewC++20 Fractionne une vue en sous-plages en fonction d’un délimiteur.
subrangeC++20 Vue d’une partie des éléments d’une plage, telle que définie par un itérateur de début et une sentinelle.
take_viewC++20 Contient le nombre spécifié d’éléments pris à partir de l’avant d’une plage.
take_while_viewC++20 Contient les éléments principaux d’une plage qui correspondent au prédicat donné.
transform_viewC++20 Vue d’une séquence sous-jacente après l’application d’une fonction de transformation à chaque élément.
values_viewC++20 Vue sur le deuxième index dans chaque valeur de type tuple dans une collection. Par exemple, en fonction d’une plage de std::tuple<string, int> valeurs, créez une vue qui se compose des int éléments de chaque tuple.

La plupart de ces classes ont des adaptateurs de plage correspondants dans l’espace std:views de noms qui créent des instances. Préférez utiliser un adaptateur pour créer une vue au lieu de créer directement des classes d’affichage. Les adaptateurs de plage sont la façon de créer des vues, sont plus faciles à utiliser et, dans certains cas, sont plus efficaces.

Afficher les caractéristiques des classes

Chaque rubrique de classe d’affichage comporte une section Caractéristiques après la section de syntaxe. La section Caractéristiques contient les entrées suivantes :

  • Adaptateur de plage : lien vers l’adaptateur de plage qui crée la vue. En règle générale, vous utilisez un adaptateur de plage pour créer un affichage plutôt que créer directement une classe d’affichage. Il est donc répertorié ici pour des raisons pratiques.

  • Plage sous-jacente : les vues ont des exigences d’itérateur différentes pour le type de plage sous-jacente qu’ils peuvent utiliser. Consultez la hiérarchie des itérateurs de plages pour plus d’informations sur les types d’itérateurs.

  • Afficher la catégorie d’itérateur : catégorie d’itérateur de la vue. Lorsqu’une vue adapte une plage, le type d’itérateur de la vue est généralement identique au type d’itérateur de la plage sous-jacente. Toutefois, il peut être différent pour certaines vues. Par exemple, reverse_view a un bidirectional_iterator, même si la plage sous-jacente a un random_access_iterator.

  • Type d’élément : type des éléments retournés par l’itérateur de la vue.

  • Taille : indique si la vue peut retourner le nombre d’éléments auxquels il fait référence. Toutes les vues ne peuvent pas être disponibles.

  • Plage commune : spécifie si la vue est un common_range, ce qui signifie que les types d’itérateur de début et de sentinelle sont identiques. Les plages courantes sont utiles pour le code de pré-plage qui fonctionne avec des paires d’itérateurs. Par exemple, il s’agit de constructeurs de paires itérateurs pour un conteneur de séquences, comme vector(ranges::begin(x), ranges::end(x)).

  • Plage empruntée : spécifie si la vue est une plage empruntée. borrowed_range<T> signifie que vous pouvez utiliser des itérateurs pour T une fois T détruit.

    Aucun conteneur standard n’est une plage empruntée, car la destruction du conteneur libère les éléments et invalide tous les itérateurs. Dans ce cas, nous disons que les itérateurs sont laissés « déranger » après destruction.

    Par exemple, std::ranges::find() retourne généralement un itérateur à l’élément trouvé dans l’argument de plage. Si l’argument de plage est un conteneur temporaire (rvalue), il s’agit d’une erreur de stocker l’itérateur retourné et de l’utiliser ultérieurement, car il est « dérangé ».

    Les algorithmes de plage qui retournent des itérateurs (ou des sous-plages) ne le font que lorsque leurs arguments sont des valeurs lvalues (non temporaires) ou des plages empruntées. Sinon, ils retournent un std::dangling objet, qui fournit un indicateur dans les messages d’erreur concernant ce qui s’est passé mal si vous avez essayé de l’utiliser comme un itérateur.

  • Itérable const: indique si vous pouvez itérer sur une const instance de la vue. Toutes les const vues ne peuvent pas être itérées. Si une vue n’est pas const itérable, vous ne pouvez pas effectuer d’itération ou for (const auto& element : as_const(theView)) la transmettre à une fonction qui prend une const référence à la vue, puis tente d’itérer dessus.

Hiérarchie d’itérateur de plages

Dans la section Caractéristiques de chaque rubrique de classe d’affichage, la catégorie itérateur dans la plage sous-jacente et la catégorie d’itérateur Afficher fait référence au type d’itérateur pris en charge par la plage/vue. Il existe six catégories d’itérateurs de plages, qui sont identifiées par les concepts C++20. La hiérarchie des itérateurs de plage, dans l’ordre croissant de capacité, est la suivante :

Concept d’itérateur de plage Description
output_range Écriture seule, avance uniquement ; passe unique.
input_range En lecture seule, avance uniquement ; passe unique.
forward_range Avance uniquement ; passe multiple.
bidirectional_range Peut avancer et reculer ; passe multiple.
random_access_range Peut accéder à la collection avec un index ; passe multiple.
contiguous_range Peut accéder à la collection avec un index et les éléments sont stockés contiguëment en mémoire.

En règle générale, un itérateur a la capacité des itérateurs qui l’précèdent dans la table. Par exemple, bidirectional_range a les fonctionnalités de forward_range, mais pas inversement. Sauf input_range, qui n’a pas la capacité de output_range car vous ne pouvez pas écrire dans un input_range.

L’instruction « nécessite input_range ou supérieure » signifie que la vue peut être utilisée avec un itérateur , ou bidirectional_rangecontiguous_rangerandom_access_rangeun input_range, car forward_rangeils sont tous aussi capables que .input_range

La hiérarchie d’itérateur de plages est directement liée à la hiérarchie d’itérateur. Pour plus d’informations, consultez les concepts d’itérateur.

Voir aussi

<ranges>
Adaptateurs de plage