Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Les concepts sont une fonctionnalité de langage C++20 qui limite les paramètres de modèle au moment de la compilation. Ils empêchent l’instanciation incorrecte du modèle, spécifient les exigences d’argument de modèle dans un formulaire lisible et fournissent des erreurs de compilateur liées au modèle plus succinctes.
Prenons l’exemple suivant, qui définit un concept pour empêcher l’instanciation d’un modèle avec un type qui ne prend pas en charge la division :
// requires /std:c++20 or later
#include <iostream>
// Definition of dividable concept which requires
// that arguments a & b of type T support division
template <typename T>
concept dividable = requires (T a, T b)
{
a / b;
};
// Apply the concept to a template.
// The template will only be instantiated if argument T supports division.
// This prevents the template from being instantiated with types that don't support division.
// This could have been applied to the parameter of a template function, but because
// most of the concepts in the <ranges> library are applied to classes, this form is demonstrated.
template <class T> requires dividable<T>
class DivideEmUp
{
public:
T Divide(T x, T y)
{
return x / y;
}
};
int main()
{
DivideEmUp<int> dividerOfInts;
std::cout << dividerOfInts.Divide(6, 3); // outputs 2
// The following line will not compile because the template can't be instantiated
// with char* because char* can be divided
DivideEmUp<char*> dividerOfCharPtrs; // compiler error: cannot deduce template arguments
}
Lorsque vous passez le commutateur /diagnostics:caret du compilateur à Visual Studio 2022 version 17.4 preview 4 ou ultérieure, l’erreur évaluée dividable<char*> à false pointe directement vers la condition d’expression (a / b) qui a échoué.
Les concepts de plage sont définis dans l’espace std::ranges de noms et déclarés dans le fichier d’en-tête <ranges> . Ils sont utilisés dans les déclarations des adaptateurs de plage, des vues, et ainsi de suite.
Il existe six catégories de plages. Ils sont liés aux catégories d’itérateurs répertoriés dans <iterator> les concepts. Dans l’ordre d’augmentation des capacités, les catégories sont les suivantes :
| Concept de plage | Description |
|---|---|
output_rangeinput_range |
Spécifie une plage dans laquelle vous pouvez écrire. Spécifie une plage à partir de laquelle vous pouvez lire une seule fois. |
forward_range |
Spécifie une plage que vous pouvez lire (et éventuellement écrire) plusieurs fois. |
bidirectional_range |
Spécifie une plage que vous pouvez lire et écrire à la fois vers l’avant et vers l’arrière. |
random_access_range |
Spécifie une plage que vous pouvez lire et écrire par index. |
contiguous_range |
Spécifie une plage dont les éléments sont séquentiels en mémoire, sont de la même taille et sont accessibles à l’aide de l’arithmétique du pointeur. |
Dans le tableau précédent, les concepts sont répertoriés dans l’ordre d’augmentation des capacités. Une plage qui répond aux exigences d’un concept répond généralement aux exigences des concepts dans les lignes qui l’précèdent. Par exemple, un a random_access_range la capacité d’un bidirectional_range, , forward_rangeinput_rangeet output_range. L’exception est input_range, qui ne peut pas être écrite dans, donc elle n’a pas les fonctionnalités de output_range.
Voici d’autres concepts de plage :
| Concept de plage | Description |
|---|---|
rangeC++20 |
Spécifie un type qui fournit un itérateur et une sentinelle. |
borrowed_rangeC++20 |
Spécifie que la durée de vie des itérateurs de la plage n’est pas liée à la durée de vie de la plage. |
common_rangeC++20 |
Spécifie que le type de l’itérateur de la plage et le type de la sentinelle de la plage sont identiques. |
Simple_ViewC++20 |
Pas un concept officiel défini dans le cadre de la bibliothèque standard, mais utilisé comme concept d’assistance sur certaines interfaces. |
sized_rangeC++20 |
Spécifie une plage qui peut fournir son nombre d’éléments efficacement. |
viewC++20 |
Spécifie un type qui a un déplacement efficace (temps constant) de construction, d’affectation et de destruction. |
viewable_rangeC++20 |
Spécifie un type qui est une vue ou peut être converti en un. |
bidirectional_range
A bidirectional_range prend en charge la lecture et l’écriture de la plage vers l’avant et vers l’arrière.
template<class T>
concept bidirectional_range =
forward_range<T> && bidirectional_iterator<iterator_t<T>>;
Paramètres
T
Type à tester pour voir s’il s’agit d’un bidirectional_range.
Notes
Ce type de plage prend en charge bidirectional_iterator ou supérieur.
A bidirectional_iterator a les capacités d’un forward_iterator, mais peut également itérer vers l’arrière.
Quelques exemples d’un bidirectional_range sont std::set, std::vectoret std::list.
borrowed_range
Un modèle de type borrowed_range si la validité des itérateurs que vous obtenez à partir de l’objet peut survivre à la durée de vie de l’objet. Autrement dit, les itérateurs d’une plage peuvent être utilisés même quand la plage n’existe plus.
template<class T>
concept borrowed_range =
range<T> &&
(is_lvalue_reference_v<T> || enable_borrowed_range<remove_cvref_t<T>>);
Paramètres
T
Type à tester pour voir s’il s’agit d’un borrowed_range.
Notes
La durée de vie d’une plage rvalue peut se terminer par un appel de fonction, que les modèles borrowed_range de plage soient ou non. S’il s’agit d’un borrowed_range, vous pouvez continuer à utiliser les itérateurs avec un comportement bien défini, quel que soit le moment de la fin de la durée de vie de la plage.
Les cas où cela n’est pas vrai sont, par exemple, pour les conteneurs comme vector ou list parce que lorsque la durée de vie du conteneur se termine, les itérateurs font référence aux éléments qui ont été détruits.
Vous pouvez continuer à utiliser les itérateurs pour un borrowed_range, par exemple, pour un view type dont iota_view<int>{0, 42} les itérateurs sont sur un ensemble de valeurs qui ne sont pas soumises à la destruction, car elles sont générées à la demande.
common_range
Le type de l’itérateur pour un common_range est identique au type de la sentinelle. Autrement dit, begin() et end() renvoyer le même type.
template<class T>
concept common_range =
ranges::range<T> && std::same_as<ranges::iterator_t<T>, ranges::sentinel_t<T>>;
Paramètres
T
Type à tester pour voir s’il s’agit d’un common_range.
Notes
L’obtention du type std::ranges::begin() et std::ranges::end() est importante pour les algorithmes qui calculent la distance entre deux itérateurs et pour les algorithmes qui acceptent des plages indiquées par des paires d’itérateurs.
Les conteneurs standard (par exemple, vector) répondent aux exigences de common_range.
contiguous_range
Les éléments d’un contiguous_range sont stockés séquentiellement en mémoire et sont accessibles à l’aide de l’arithmétique du pointeur. Par exemple, un tableau est un contiguous_range.
template<class T>
concept contiguous_range =
random_access_range<T> && contiguous_iterator<iterator_t<T>> &&
requires(T& t) {{ ranges::data(t) } -> same_as<add_pointer_t<range_reference_t<T>>>;};
Paramètres
T
Type à tester pour voir s’il s’agit d’un contiguous_range.
Notes
Un contiguous_range accès est accessible par arithmétique du pointeur, car les éléments sont disposés séquentiellement en mémoire et sont de la même taille. Ce type de plage prend en charge continguous_iterator, qui est le plus flexible de tous les itérateurs.
Quelques exemples d’un contiguous_range sont std::array, std::vectoret std::string.
Exemple : contiguous_range
L’exemple suivant montre l’utilisation d’un pointeur arithmétique pour accéder à un contiguous_range:
// requires /std:c++20 or later
#include <ranges>
#include <iostream>
#include <vector>
int main()
{
// Show that vector is a contiguous_range
std::vector<int> v = {0,1,2,3,4,5};
std::cout << std::boolalpha << std::ranges::contiguous_range<decltype(v)> << '\n'; // outputs true
// Show that pointer arithmetic can be used to access the elements of a contiguous_range
auto ptr = v.data();
ptr += 2;
std::cout << *ptr << '\n'; // outputs 2
}
true
2
forward_range
Une forward_range prise en charge de la lecture (et éventuellement de l’écriture) de la plage plusieurs fois.
template<class T>
concept forward_range = input_range<T> && forward_iterator<iterator_t<T>>;
Paramètres
T
Type à tester pour voir s’il s’agit d’un forward_range.
Notes
Ce type de plage prend en charge forward_iterator ou supérieur. Un forward_iterator peut itérer sur une plage plusieurs fois.
input_range
Il input_range s’agit d’une plage qui peut être lue à partir d’une seule fois.
template<class T>
concept input_range = range<T> && input_iterator<iterator_t<T>>;
Paramètres
T
Type à tester pour voir s’il s’agit d’un input_range.
Notes
Lorsqu’un type répond aux exigences de input_range:
- La
ranges::begin()fonction retourne uninput_iterator. Appelerbegin()plusieurs fois sur uninput_rangecomportement non défini. - Vous pouvez déréférencer une
input_iteratorvaleur répétée, ce qui génère la même valeur à chaque fois. Uninput_rangen’est pas multi-passe. L’incrémentation d’un itérateur invalide toutes les copies. - Il peut être utilisé avec
ranges::for_each. - Il prend en charge
input_iteratorou supérieur.
output_range
Il output_range s’agit d’une plage dans laquelle vous pouvez écrire.
template<class R, class T>
concept output_range = range<R> && output_iterator<iterator_t<R>, T>;
Paramètres
R
Type de la plage.
T
Type des données à écrire dans la plage.
Notes
La signification est output_iterator<iterator_t<R>, T> que le type fournit un itérateur qui peut écrire des valeurs de type T dans une plage de type R. En d’autres termes, il prend en charge output_iterator ou supérieur.
random_access_range
Un random_access_range peut lire ou écrire une plage par index.
template<class T>
concept random_access_range =
bidirectional_range<T> && random_access_iterator<iterator_t<T>>;
Paramètres
T
Type à tester pour voir s’il s’agit d’un sized_range.
Notes
Ce type de plage prend en charge random_access_iterator ou supérieur. A random_access_range a les capacités d’un input_range, , output_rangeforward_range, et bidirectional_range. A random_access_range est triable.
Quelques exemples d’un random_access_range sont std::vector, std::arrayet std::deque.
range
Définit les exigences qu’un type doit respecter pour qu’il s’agit d’un range. Un range itérateur fournit un itérateur et une sentinelle, afin que vous puissiez itérer sur ses éléments.
template<class T>
concept range = requires(T& rg)
{
ranges::begin(rg);
ranges::end(rg);
};
Paramètres
T
Type à tester pour voir s’il s’agit d’un range.
Notes
Les exigences d’un range sont les suivantes :
- Il peut être itéré à l’aide
std::ranges::begin()etstd::ranges::end() ranges::begin()etranges::end()s’exécutent en temps constant amorti et ne modifient pas lerange. Le temps constant amorti ne signifie pas O(1), mais que le coût moyen sur une série d’appels, même dans le pire des cas, est O(n) plutôt que O(n^2) ou pire.[ranges::begin(), ranges::end())indique une plage valide.
Simple_View
Il Simple_View s’agit d’un concept d’exposition uniquement utilisé sur certaines ranges interfaces. Elle n’est pas définie dans la bibliothèque. Il est utilisé uniquement dans la spécification pour décrire le comportement de certains adaptateurs de plage.
template<class V>
concept Simple_View = // exposition only
ranges::view<V> && ranges::range<const V> &&
std::same_as<std::ranges::iterator_t<V>, std::ranges::iterator_t<const V>> &&
std::same_as<std::ranges::sentinel_t<V>, std::ranges::sentinel_t<const V>>;
Paramètres
V
Type à tester pour voir s’il s’agit d’un Simple_View.
Notes
Une vue V est une Simple_View valeur si toutes les valeurs suivantes sont vraies :
Vest une vueconst Vest une plage- Les deux
vetconst Vont les mêmes types d’itérateurs et de sentinelles.
sized_range
A sized_range fournit le nombre d’éléments de la plage dans le temps constant amorti.
template<class T>
concept sized_range = range<T> &&
requires(T& t) { ranges::size(t); };
Paramètres
T
Type à tester pour voir s’il s’agit d’un sized_range.
Notes
Les exigences d’un sized_range sont celles qui l’appellent ranges::size :
- Ne modifie pas la plage.
- Retourne le nombre d’éléments dans le temps constant amorti. Le temps constant amorti ne signifie pas O(1), mais que le coût moyen sur une série d’appels, même dans le pire des cas, est O(n) plutôt que O(n^2) ou pire.
Quelques exemples d’un sized_range sont std::list et std::vector.
Exemple : sized_range
L’exemple suivant montre qu’il s’agit d’un vector sized_range:int
// requires /std:c++20 or later
#include <ranges>
#include <iostream>
#include <vector>
int main()
{
std::cout << std::boolalpha << std::ranges::sized_range<std::vector<int>> << '\n'; // outputs "true"
}
view
Un view a un déplacement constant de la construction, de l’affectation et des opérations de destruction, quel que soit le nombre d’éléments qu’il possède. Il n’est pas nécessaire de copier des vues constructibles ou de copier assignables, mais si elles le sont, ces opérations doivent également s’exécuter en temps constant.
En raison de l’exigence de temps constant, vous pouvez composer efficacement des vues. Par exemple, étant donné un vecteur d’appel int input, une fonction qui détermine si un nombre est divisible par trois et une fonction qui place un nombre, l’instruction auto x = input | std::views::filter(divisible_by_three) | std::views::transform(square); produit efficacement une vue qui contient les carrés des nombres dans l’entrée qui sont divisibles par trois. La connexion de vues avec | est appelée composition des vues. Si un type satisfait au view concept, il peut être composé efficacement.
template<class T>
concept view = ranges::range<T> && std::movable<T> && ranges::enable_view<T>;
Paramètres
T
Type à tester pour voir s’il s’agit d’une vue.
Notes
L’exigence essentielle qui rend une vue composable est qu’il est bon marché de déplacer/copier. Cela est dû au fait que la vue est déplacée/copiée lorsqu’elle est composée avec une autre vue. Il doit s’agir d’une plage mobile.
ranges::enable_view<T> est une caractéristique utilisée pour revendiquer la conformité aux exigences sémantiques du view concept. Un type peut choisir par :
- dérivant publiquement et sans ambiguïté d’une spécialisation de
ranges::view_interface - dérivant publiquement et sans ambiguïté de la classe
ranges::view_basevide , ou ranges::enable_view<T>spécialisé danstrue
L’option 1 est préférée, car view_interface fournit également une implémentation par défaut qui enregistre du code réutilisable que vous devez écrire.
En cas d’échec, l’option 2 est un peu plus simple que l’option 3.
L’avantage de l’option 3 est qu’il est possible sans modifier la définition du type.
viewable_range
Il viewable_range s’agit d’un type qui est une vue ou peut être converti en un.
template<class T>
concept viewable_range =
range<T> && (borrowed_range<T> || view<remove_cvref_t<T>>);
Paramètres
T
Type à tester pour voir s’il s’agit d’une vue ou peut être converti en un.
Notes
Permet std::ranges::views::all() de convertir une plage en vue.