web-dev-qa-db-fra.com

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

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

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

95
Rayniery

Les informations suivantes sont obsolètes. Il doit être mis à jour conformément à la dernière version de Concepts Lite.

La section 3 de la proposition de contraintes couvre cela de manière assez approfondie.

La proposition de concepts a été mise en veilleuse pendant un court instant 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 sont une partie de la proposition de concepts et sont un bloc de construction nécessaire dans 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 leur signification:

  1. Contrainte - 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 des 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 concernent 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);

Nous définissons ici un modèle de fonction appelé sort. Le nouvel ajout est la clause requiert. La clause requires donne des contraintes sur les arguments du modèle pour cette fonction. En particulier, cette contrainte indique que le type Cont doit être un type Sortable. Une chose intéressante est qu'elle peut être écrite sous une forme plus concise comme:

template <Sortable Cont>
void sort(Cont& container);

Maintenant, si vous essayez de passer tout ce qui n'est pas considéré comme Sortable à cette fonction, vous obtiendrez une erreur Nice 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 de à l'intérieur la fonction sort 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 modèle et vous donnent des informations à ce sujet. Les contraintes tentent de répondre aux types de questions suivants sur le type:

  1. Est-ce que tel ou tel opérateur est surchargé?
  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-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 à utiliser comme cela?

Cependant, les contraintes ne sont pas destinées à remplacer les caractères de type. Au lieu de cela, ils travailleront main dans la main. Certains traits de type peuvent désormais ê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. Voici quelques bons exemples de contraintes:

  • Equality_comparable<T>: Vérifie si le type contient == Avec 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 un spécial concepts-lite build de GCC .


Au-delà de Concepts-Lite

Maintenant, nous entrons dans tout ce qui dépasse la proposition 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 les relations, les invariants, les garanties de complexité et d'autres choses de ce genre. Regardons un exemple.

Bien que la contrainte Equality_comparable<T,U> Vous indique qu'il existe un operator== Qui accepte les types T et U, il ne vous indique pas ce que cette opération moyen. Pour cela, nous aurons l'axiome Equivalence_relation. Cet axiome dit que lorsque des objets de ces deux types sont comparés à operator== Donnant 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 à la place comme un operator<. Vous seriez méchant à le faire, mais vous le pourriez.

Un autre exemple est un axiome Greater. C'est bien beau de dire que deux objets de type T peuvent être comparés aux opérateurs > Et <, Mais que font-ils signifie ? L'axiome Greater indique que si x est supérieur à y, alors y est inférieur à x. La spécification proposée tel un 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 à ces sémantiques.

Exemples

Voici quelques exemples courants d'axiomes:

  • Equivalence_relation: Si deux objets comparent ==, Ils sont équivalents.

  • Greater: Chaque fois que x > y, Alors y < x.

  • Less_equal: Chaque fois que x <= y, Puis !(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 toujours x == y (C'est-à-dire qu'il n'est pas 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.

Par exemple, considérons le concept Ordered 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 concept Regular. Le concept Regular 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, le Ordered requiert que T réponde à une contrainte et à quatre axiomes:

  • Contrainte: Un type Ordered doit avoir un operator<. Ceci est vérifié statiquement donc il doit exister.
  • Axiomes: Pour x et y de type T:
    • x < y Donne une commande totale stricte.
    • Lorsque x est supérieur à y, y est inférieur à x, et vice versa.
    • Lorsque x est inférieur ou égal à y, y n'est pas inférieur à x, et vice versa.
    • Lorsque x est supérieur ou égal à y, y n'est pas supérieur à x, et vice versa.

La combinaison de contraintes et d'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 les concepts, nous serons en mesure de garantir que les exigences sont remplies.

Dans la dernière conception des concepts , le compilateur vérifie uniquement que les exigences syntaxiques d'un concept sont remplies par l'argument modèle. Les axiomes ne sont pas contrôlés. Comme les axiomes dénotent une sémantique qui n'est pas statiquement évaluable (ou souvent impossible à vérifier entièrement), l'auteur d'un type devrait déclarer explicitement que son type répond à toutes les exigences d'un concept. Ceci était connu sous le nom de mappage de concept dans les conceptions précédentes mais a depuis été supprimé.

Exemples

Voici quelques exemples de concepts:

  • Les types Regular sont constructibles, destructibles, copiables et peuvent être comparés.

  • Les types Ordered prennent en charge operator< Et ont une commande totale stricte et d'autres sémantiques de commande.

  • Les types Copyable sont constructibles, destructibles et si x est égal à y et x est copié, la copie sera également comparable à y.

  • Les types Iterator 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 prendre en charge 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 sont une étape très importante, car ils fournissent les exigences de types applicables de manière statique afin que nous puissions écrire des fonctions et des classes de modèles beaucoup plus propres. Maintenant, nous pouvons éviter certaines des difficultés et de la laideur de std::enable_if Et de ses amis métaprogrammeurs.

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 ses types comme répondant à certaines contraintes. Ils sont vérifiés statiquement et utilisent de simples fonctionnalités de langage au moment de la compilation.

  3. Les implémentations des 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 complète serait en mesure de le faire.

La proposition de contraintes a été conçue spécifiquement pour qu'une proposition de concepts complète puisse être introduite par-dessus. Avec un peu de chance, cette transition devrait être assez fluide. Le groupe de concepts cherche à introduire des contraintes pour C++ 14 (ou dans un rapport technique peu de temps après), tandis que des concepts complets pourraient commencer à émerger autour de C++ 17.

133
Joseph Mansfield

Voir aussi "ce qui est" léger "sur les concepts légers" dans la section 2.3 des récents procès-verbaux de téléconférence (12 mars) et compte rendu des discussions, qui ont été publiés le même jour ici: http://isocpp.org/blog/2013/03/new-paper-n3576-sg8-concepts-teleconference-minutes-2013-03-12-herb-sutter .

22
Herb Sutter

Mes 2 cents:

  1. La proposition concepts-lite n'est pas destinée à faire une "vérification de type" du modèle implémentation. C'est-à-dire que Concepts-lite assurera (théoriquement) la compatibilité de l'interface au niveau du site d'instanciation du modèle. Citation de 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 de archtypes lorsque vous parlez de concepts-lite. les archtypes, si je me souviens bien, dans la proposition de concepts lourds sont des types qui n'offrent ni moins ni plus pour satisfaire implémentation du modèle.

  2. concepts-lite utilise des fonctions constexpr glorifiées avec une astuce de syntaxe prise en charge par le compilateur. Aucun changement dans les règles de recherche.

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

  4. Enfin, citant à nouveau "La proposition de contraintes ne concerne pas directement la spéci fi cation ou l'utilisation de la sémantique; elle vise uniquement à vérifier la syntaxe." Cela signifierait que les axiomes ne sont pas dans le champ d'application (jusqu'à présent).

4
Sumant