Quelles sont les différences entre les concepts et les contraintes de modèle?

96

Je veux savoir quelles sont les différences sémantiques entre la proposition de concepts complets de C ++ et les contraintes de modèle (par exemple, les contraintes apparaissant dans Dlang ou la nouvelle proposition de concepts-lite pour C ++ 1y ).

Que sont les concepts à part entière capables de faire que les contraintes de modèle ne peuvent pas faire?

Rayniery
la source
2
La proposition de contraintes va dans ce sens.
chris
Je me souvenais de 4.8 Concepts de conception , mais il n'y a en fait pas beaucoup d'énumérations sur la manière dont les concepts ajoutent des contraintes. Si quoi que ce soit, les concepts de Google peuvent maintenant révéler des différences facilement repérables après avoir acquis une meilleure compréhension des contraintes de la proposition.
chris
Les concepts IMHO améliorent la lisibilité et fournissent des capacités programmatiques plus claires, comme demandé il y a longtemps par Alexander Stepanov dans «Elements of Programming». La proposition allégée est juste un pas dans ce sens pour alléger le fardeau des étranges contraintes de type enable_if qui sont requises pour le moment. Le plus tôt sera le mieux pour la programmation générique.
dirvine

Réponses:

135

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:

  1. Contrainte - Un prédicat sur les propriétés statiquement évaluables d'un type. Exigences purement syntaxiques. Pas une abstraction de domaine.
  2. Axiomes - Exigences sémantiques de types supposés vrais. Non vérifié statiquement.
  3. 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 Contdoit être un Sortabletype. 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 Tn'est pas un Sortabletype. Si vous aviez fait cela en C ++ 11, vous auriez eu une horrible erreur lancée de l' intérieur de la sortfonction 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:

  1. Ce type a-t-il surchargé tel ou tel opérateur?
  2. Ces types peuvent-ils être utilisés comme opérandes pour cet opérateur?
  3. Ce type a-t-il tel ou tel trait?
  4. Cette expression constante est-elle égale à cela? (pour les arguments de modèle non de type)
  5. Ce type a-t-il une fonction appelée yada-yada qui renvoie ce type?
  6. 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 Tet 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 Greateraxiome. C'est bien beau de dire que deux objets de type Tpeuvent être comparés avec >et< opérateurs , mais que signifient- ils ? L' Greateraxiome dit que si xest alors plus grand y, alors yest 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:

  1. Ces deux opérateurs ont-ils cette relation l'un avec l'autre?
  2. Est-ce que cet opérateur pour tel ou tel type signifie cela?
  3. Cette opération sur ce type a-t-elle cette complexité?
  4. 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 xet yde type T: si x == y, un nouvel objet du même type créé par construction de copie T{x} == yet 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 Orderedconcept 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 Tsoit Ordered, il doit également répondre aux exigences du Regularconcept. Le Regularconcept 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 Orderedqui Trépondent à une contrainte et quatre axiomes:

  • Contrainte: un Orderedtype doit avoir un operator<. Ceci est vérifié statiquement donc il doit exister.
  • Axiomes: Pour xet yde type T:
    • x < y donne un ordre total strict.
    • Quand xest supérieur à y, yest inférieur à xet vice versa.
    • Quand xest inférieur ou égal à y, yn'est pas inférieur àx , et vice versa.
    • Quand xest supérieur ou égal à y, yn'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.

  • Orderedles types prennent en charge operator<, et ont un ordre total strict et d'autres sémantiques d'ordre.

  • Copyableles types sont constructibles par copie, destructibles, et si xest égal à yet xest copié, la copie sera également comparée égale à y.

  • Iteratorles types doivent avoir des types associés value_type,reference , difference_typeet iterator_categoryqui 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_ifet de ses amis en métaprogrammation.

Cependant, il y a un certain nombre de choses que la proposition de contraintes ne fait pas:

  1. Il ne fournit pas de langage de définition de concept.

  2. 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.

  3. 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.

sftrabbit
la source
5
Il convient de noter que concepts-lite ne vérifie pas les contraintes par rapport à l'implémentation du modèle lui-même. Vous pouvez donc prétendre que n'importe quel DefaultConstructable peut être utilisé, mais le compilateur ne vous empêchera pas d'utiliser un constructeur de copie par accident. Une proposition de concepts plus complète le ferait.
Nicol Bolas
24
C'est une première ébauche?!
Nicol Bolas
2
@sftrabbit, très bonne réponse. Mais j'ai une question: comment un compilateur va vérifier qu'un type remplit les exigences sémantiques d'un concept?
Rayniery
1
Est-ce que (avec beaucoup d'incertitude) les «axiomes» seront vérifiés pendant l'exécution, ou serviront-ils uniquement de balises de promesse?
Red XIII
4
@ScarletAmaranth: Parce que cela revient à prouver automatiquement un théorème en temps limité limité. Il y a deux obstacles à cela: 1. Prouver un théorème en temps limité limité qui est extrêmement difficile en théorie, et impossible avec la technologie actuelle. 2. Vous ne pouvez pas prouver un axiome, à moins que la preuve ne soit basée sur d'autres axiomes. (Dans ce cas, cela devient mathématiquement "pas un axiome") Ces axiomes sont destinés à dire au compilateur "Bien sûr, vous pouvez inverser la comparaison si vous le jugez utile ...", ou "Oui, un EmptySet utilisé dans une intersection toujours donne le même résultat. "
Laurent LA RIZZA
4

Mes 2 cents:

  1. La proposition concepts-lite n'est pas destinée à effectuer une «vérification de type» de l' implémentation du modèle . C'est-à-dire que Concepts-lite assurera (théoriquement) la compatibilité de l'interface sur le site d'instanciation du modèle. Citant l'article: "concepts lite est une extension de C ++ qui permet l'utilisation de prédicats pour contraindre les arguments de modèle". Et c'est tout. Il ne dit pas que le corps du modèle sera vérifié (isolément) par rapport aux prédicats. Cela signifie probablement qu'il n'y a pas de notion de premier ordre des archtypes lorsque vous parlez de concepts-lite. Les archtypes, si je me souviens bien, dans les propositions lourdes de concepts sont des types qui n'offrent ni moins ni plus pour satisfaire la mise en œuvre du modèle.

  2. concepts-lite utilise des fonctions constexpr glorifiées avec un peu d'astuce syntaxique supportée par le compilateur. Aucune modification des règles de recherche.

  3. Les programmeurs ne sont pas tenus d'écrire des cartes de concepts.

  4. Enfin, en citant à nouveau "La proposition de contraintes n'aborde pas directement la spéci fi cation ou l'utilisation de la sémantique; elle vise uniquement la vérification de la syntaxe." Cela signifierait que les axiomes ne sont pas dans le cadre (pour l'instant).

Sumant
la source