L'information suivante est périmée. Il doit être mis à jour conformément au dernier projet de Concepts Lite.
La section 3 de la proposition de contraintes couvre ce sujet de manière suffisamment approfondie.
La proposition de concepts a été mise en veilleuse pendant un court moment dans l'espoir que les contraintes (ie concepts-lite) puissent être étoffées et mises en œuvre dans un délai plus court, visant actuellement au moins quelque chose en C ++ 14. La proposition de contraintes est conçue pour agir comme une transition en douceur vers une définition ultérieure des concepts. Les contraintes font partie de la proposition de concepts et sont un élément essentiel de sa définition.
Dans Design of Concept Libraries for C ++ , Sutton et Stroustrup considèrent la relation suivante:
Concepts = Contraintes + Axiomes
Pour résumer rapidement leurs significations:
- Contrainte - Un prédicat sur les propriétés statiquement évaluables d'un type. Exigences purement syntaxiques. Pas une abstraction de domaine.
- Axiomes - Exigences sémantiques de types supposés vrais. Non vérifié statiquement.
- Concepts - Exigences générales et abstraites des algorithmes sur leurs arguments. Défini en termes de contraintes et d'axiomes.
Donc, si vous ajoutez des axiomes (propriétés sémantiques) aux contraintes (propriétés syntaxiques), vous obtenez des concepts.
Concepts-Lite
La proposition concepts-lite ne nous apporte que la première partie, les contraintes, mais c'est une étape importante et nécessaire vers des concepts à part entière.
Contraintes
Les contraintes sont toutes liées à la syntaxe . Ils nous donnent un moyen de discerner statiquement les propriétés d'un type au moment de la compilation, afin que nous puissions restreindre les types utilisés comme arguments de modèle en fonction de leurs propriétés syntaxiques. Dans la proposition actuelle de contraintes, elles sont exprimées avec un sous-ensemble de calcul propositionnel utilisant des connecteurs logiques comme &&
et ||
.
Jetons un œil à une contrainte en action:
template <typename Cont>
requires Sortable<Cont>()
void sort(Cont& container);
Ici, nous définissons un modèle de fonction appelé sort
. Le nouvel ajout est la clause requiert . La clause requires donne certaines contraintes sur les arguments du modèle pour cette fonction. En particulier, cette contrainte indique que le type Cont
doit être un Sortable
type. Une chose intéressante est qu'il peut être écrit sous une forme plus concise comme:
template <Sortable Cont>
void sort(Cont& container);
Maintenant, si vous essayez de passer quelque chose qui n'est pas considéré Sortable
à cette fonction, vous obtiendrez une belle erreur qui vous indiquera immédiatement que le type déduit pour T
n'est pas un Sortable
type. Si vous aviez fait cela en C ++ 11, vous auriez eu une horrible erreur lancée de l' intérieur de la sort
fonction qui n'a de sens pour personne.
Les prédicats de contraintes sont très similaires aux traits de type. Ils prennent un type d'argument de modèle et vous donnent des informations à ce sujet. Les contraintes tentent de répondre aux types de questions suivants sur le type:
- Ce type a-t-il surchargé tel ou tel opérateur?
- Ces types peuvent-ils être utilisés comme opérandes pour cet opérateur?
- Ce type a-t-il tel ou tel trait?
- Cette expression constante est-elle égale à cela? (pour les arguments de modèle non de type)
- Ce type a-t-il une fonction appelée yada-yada qui renvoie ce type?
- Ce type répond-il à toutes les exigences syntaxiques pour être utilisé comme ça?
Cependant, les contraintes ne visent pas à remplacer les traits de type. Au lieu de cela, ils travailleront main dans la main. Certains traits de type peuvent maintenant être définis en termes de concepts et certains concepts en termes de traits de type.
Exemples
Donc, la chose importante à propos des contraintes est qu'elles ne se soucient pas de la sémantique d'un iota. Quelques bons exemples de contraintes sont:
Equality_comparable<T>
: Vérifie si le type contient les ==
deux opérandes du même type.
Equality_comparable<T,U>
: Vérifie s'il existe un ==
avec des opérandes gauche et droit des types donnés
Arithmetic<T>
: Vérifie si le type est un type arithmétique.
Floating_point<T>
: Vérifie si le type est un type à virgule flottante.
Input_iterator<T>
: Vérifie si le type prend en charge les opérations syntaxiques qu'un itérateur d'entrée doit prendre en charge.
Same<T,U>
: Vérifie si le type donné est le même.
Vous pouvez essayer tout cela avec une version spéciale de GCC .
Au-delà des concepts-Lite
Maintenant, nous entrons dans tout ce qui va au-delà de la proposition de concepts lite. C'est encore plus futuriste que l'avenir lui-même. Tout à partir de maintenant est susceptible de changer un peu.
Axiomes
Les axiomes concernent la sémantique . Ils spécifient des relations, des invariants, des garanties de complexité, etc. Regardons un exemple.
Bien que la Equality_comparable<T,U>
contrainte vous indique qu'il existe un operator==
qui prend des types T
et U
, elle ne vous dit pas ce que signifie cette opération . Pour cela, nous aurons l'axiome Equivalence_relation
. Cet axiome dit que lorsque les objets de ces deux types sont comparés au operator==
don true
, ces objets sont équivalents. Cela peut sembler redondant, mais ce n'est certainement pas le cas. Vous pouvez facilement définir un operator==
qui se comporte plutôt comme un operator<
. Vous seriez mauvais de faire ça, mais vous pourriez.
Un autre exemple est un Greater
axiome. C'est bien beau de dire que deux objets de type T
peuvent être comparés avec >
et<
opérateurs , mais que signifient- ils ? L' Greater
axiome dit que si x
est alors plus grand y
, alors y
est inférieur à x
. La spécification proposée d'un tel axiome ressemble à:
template<typename T>
axiom Greater(T x, T y) {
(x>y) == (y<x);
}
Les axiomes répondent donc aux types de questions suivants:
- Ces deux opérateurs ont-ils cette relation l'un avec l'autre?
- Est-ce que cet opérateur pour tel ou tel type signifie cela?
- Cette opération sur ce type a-t-elle cette complexité?
- Ce résultat de cet opérateur implique-t-il que cela est vrai?
Autrement dit, ils sont entièrement concernés par la sémantique des types et les opérations sur ces types. Ces choses ne peuvent pas être vérifiées statiquement. Si cela doit être vérifié, un type doit en quelque sorte proclamer qu'il adhère à cette sémantique.
Exemples
Voici quelques exemples courants d'axiomes:
Equivalence_relation
: Si deux objets se comparent ==
, ils sont équivalents.
Greater
: Chaque fois x > y
, alors y < x
.
Less_equal
: Chaque fois x <= y
, alors !(y < x)
.
Copy_equality
: Pour x
et y
de type T
: si x == y
, un nouvel objet du même type créé par construction de copie T{x} == y
et encore x == y
(c'est-à-dire non destructif).
Concepts
Désormais, les concepts sont très faciles à définir; ils sont simplement la combinaison de contraintes et d'axiomes . Ils fournissent une exigence abstraite sur la syntaxe et la sémantique d'un type.
À titre d'exemple, considérons le Ordered
concept suivant :
concept Ordered<Regular T> {
requires constraint Less<T>;
requires axiom Strict_total_order<less<T>, T>;
requires axiom Greater<T>;
requires axiom Less_equal<T>;
requires axiom Greater_equal<T>;
}
Notez tout d'abord que pour que le type de modèle T
soit Ordered
, il doit également répondre aux exigences du Regular
concept. Le Regular
concept est une exigence très basique que le type se comporte bien - il peut être construit, détruit, copié et comparé.
En plus de ces exigences, les exigences Ordered
qui T
répondent à une contrainte et quatre axiomes:
- Contrainte: un
Ordered
type doit avoir un operator<
. Ceci est vérifié statiquement donc il doit exister.
- Axiomes: Pour
x
et y
de type T
:
x < y
donne un ordre total strict.
- Quand
x
est supérieur à y
, y
est inférieur à x
et vice versa.
- Quand
x
est inférieur ou égal à y
, y
n'est pas inférieur àx
, et vice versa.
- Quand
x
est supérieur ou égal à y
, y
n'est pas supérieur à x
, et vice versa.
Combiner des contraintes et des axiomes comme celui-ci vous donne des concepts. Ils définissent les exigences syntaxiques et sémantiques pour les types abstraits à utiliser avec les algorithmes. Les algorithmes doivent actuellement supposer que les types utilisés prendront en charge certaines opérations et exprimeront certaines sémantiques. Avec des concepts, nous pourrons nous assurer que les exigences sont respectées.
Dans la conception des derniers concepts , le compilateur vérifiera uniquement que les exigences syntaxiques d'un concept sont remplies par l'argument modèle. Les axiomes ne sont pas contrôlés. Puisque les axiomes désignent une sémantique qui ne sont pas statiquement évaluables (ou souvent impossibles à vérifier entièrement), l'auteur d'un type devrait déclarer explicitement que leur type répond à toutes les exigences d'un concept. Ceci était connu sous le nom de mappage conceptuel dans les conceptions précédentes, mais a depuis été supprimé.
Exemples
Voici quelques exemples de concepts:
Regular
les types sont constructibles, destructibles, copiables et peuvent être comparés.
Ordered
les types prennent en charge operator<
, et ont un ordre total strict et d'autres sémantiques d'ordre.
Copyable
les types sont constructibles par copie, destructibles, et si x
est égal à y
et x
est copié, la copie sera également comparée égale à y
.
Iterator
les types doivent avoir des types associés value_type
,reference
, difference_type
et iterator_category
qui eux - mêmes doivent répondre à certains concepts. Ils doivent également soutenir operator++
et être déréférencables.
La route des concepts
Les contraintes sont la première étape vers une fonctionnalité de concepts complets de C ++. Ils constituent une étape très importante, car ils fournissent les exigences statiquement applicables des types afin que nous puissions écrire des fonctions et des classes de modèle beaucoup plus propres. Maintenant, nous pouvons éviter certaines des difficultés et la laideur de std::enable_if
et de ses amis en métaprogrammation.
Cependant, il y a un certain nombre de choses que la proposition de contraintes ne fait pas:
Il ne fournit pas de langage de définition de concept.
Les contraintes ne sont pas des cartes conceptuelles. L'utilisateur n'a pas besoin d'annoter spécifiquement leurs types comme répondant à certaines contraintes. Ils sont vérifiés statiquement et utilisent des fonctionnalités de langage de compilation simples.
Les implémentations de modèles ne sont pas contraintes par les contraintes sur leurs arguments de modèle. Autrement dit, si votre modèle de fonction fait quelque chose avec un objet de type contraint qu'il ne devrait pas faire, le compilateur n'a aucun moyen de diagnostiquer cela. Une proposition de concepts complets serait en mesure de le faire.
La proposition de contraintes a été conçue spécifiquement pour qu'une proposition complète de concepts puisse y être ajoutée. Avec un peu de chance, cette transition devrait être une conduite assez douce. Le groupe de concepts cherche à introduire des contraintes pour C ++ 14 (ou dans un rapport technique peu après), tandis que des concepts complets pourraient commencer à émerger autour de C ++ 17.