Pourquoi avons-nous besoin nécessite?
L'un des coins de C++20 concepts est qu'il y a certaines situations dans lesquelles vous devez écrire requires requires
. Par exemple, cet exemple de [expr.prim.req]/3:
Un nécessite expression peut également être utilisé dans un exige la clause ([temp]) comme un moyen de rédaction ad hoc des contraintes sur le modèle des arguments tels que celui ci-dessous:
template<typename T> requires requires (T x) { x + x; } T add(T a, T b) { return a + b; }
La nécessite d'abord introduit la exige la clause, et la seconde partie présente le nécessite expression.
Qu'est-ce que la technique la raison derrière le besoin de deuxième requires
mot-clé? Pourquoi ne peut-on pas autoriser l'écriture:
template<typename T>
requires (T x) { x + x; }
T add(T a, T b) { return a + b; }
(Note: veuillez ne pas répondre que de la grammaire requires
c')
- Semble
requires
peut être utilisé pour la méthode de la classe de modèle, et il serait "ambigu"template <typename T> struct S {void f(T t) requires requires (T x) {x + x;} { t + t;} };
- Suggestion: "Est-il rien qui exige exige exige?". Plus sérieusement, j'ai l'intuition que c'est la même raison derrière
noexcept(noexcept(...))
. - Ils disent que "Le nécessite tout d'abord introduit la nécessite de la clause, et la seconde partie présente la nécessite d'expression.", mais ce n'est pas compatible avec la grammaire, ils donnent juste au-dessus
- Avec
noexcept
il y a ambiguïté.noexcept(f())
pourrait signifier pour êtrenoexcept
sif()
prend la valeur vrai ou sif()
estnoexcept
. - Bien, alors, n'est-ce pas la même raison ici? Un
requires
clause peut avoir n'importe quel constexpr prédicat, non? - Une raison que je vois pour le faire, c'est de rendre l'analyse plus facile. Lorsque le compilateur voit
requires
il sait le prochain symbole est l'exigence, de sorte que nous avons besoin de la supplémentairesrequires
à dire qu'il doit analyser cette exigence ad-hoc exigence. - Les deux
requires
sont des homonymes à mon avis: ils ont le même aspect, sort la même, la même odeur, mais sont intrinsèquement différents. Si je devais proposer un correctif, je vous suggère de renommer l'un d'eux. - Apparemment oui, comme l'a démontré par NicolBolas, bien que dans une moindre mesure.
co_requires
? (Désolé, pas pu résister).- Où va la folie arrêter? La prochaine chose que vous savez, nous aurons
long long
. - nécessite nécessaire?" aurait été encore plus allitératifs!!
- Ce n'est pas un burnination demande lol
- Il est logique gramatically, bizarrement assez.
add<T>()
nécessite unT
qui exige à son tour la capacité d'être ajouté à d'autresT
s. Ainsi, alors qu'il semble répétitif, c'est logique, si vous pensez à ce sujet assez. (Où "je dois être addable" est l'expression, et "la fonction a besoin de quelque chose qui doit être addable" est la contrainte.) - Parce que
require
nécessiterequires requires
. Désolé, je ne pouvais pas résister. - je ne regarde jamais la parole exiger la même chose de nouveau
- et puis nous avons modèle des paramètres de modèle...
- A la question de savoir pourquoi avons-nous besoin nécessite requis requis? (Dang, maintenant, je suis en train de réfléchir buffalo ...)
- dans l'Ouest-de-l'Angleterre rurale accent on pourrait même dire "Pourquoi ne nous exige exige exige?" 😉
- Merci pour ce mal de tête... je n'ai pas besoin...
- Par l'imbrication
requires
clauses/expressions que vous obtenez de drôles de constructions.void Fn() requires requires () { requires requires () { requires true; }; } {}
est une contrainte de fonctionner.
Vous devez vous connecter pour publier un commentaire.
C'est parce que la grammaire exige. Il n'.
Un
requires
contrainte de ne pas ont pour utiliser unrequires
expression. Il peut utiliser plus ou moins arbitraire boolean expression constante. Par conséquent,requires (foo)
doit être légitimerequires
contrainte.Un
requires
expression (chose qui teste si certaines choses suivre certaines contraintes) est distincte de construire, il est juste introduit par le même mot clé.requires (foo f)
serait le début d'un permis derequires
expression.Ce que vous voulez, c'est que si vous utilisez
requires
dans un endroit qui accepte les contraintes, vous devriez être en mesure de faire une "contrainte+expression" hors de larequires
clause.Alors, voici la question: si vous mettez
requires (foo)
dans un endroit qui est approprié pour une exige de contrainte... dans quelle mesure l'analyseur avoir à aller de l'avant, on peut comprendre que c'est une nécessite contrainte plutôt qu'une contrainte+expression de la façon dont vous voulez qu'il soit?Considérez ceci:
Si
foo
est un type, puis(foo)
est une liste de paramètres d'un nécessite d'expression, et tout le{}
n'est pas le corps de la fonction, mais le corps de cetterequires
expression. Sinon,foo
est une expression dans unrequires
clause.Eh bien, vous pourriez dire que le compilateur doit juste comprendre ce
foo
est le premier. Mais le C++ vraiment n'aime pas lorsque l'acte de base de l'analyse d'une séquence de jetons exige que le compilateur comprendre ce que ces identifiants dire avant qu'il peut faire sens des jetons. Oui, le C++ est sensible au contexte, de sorte que cela ne se produise. Mais le comité préfère éviter, lorsque cela est possible.Donc oui, c'est la grammaire.
x
est. Un autre exemple est l'ADL de travail avec les modèles de fonction seulement si il y a une fonction semblable de modèle portée (démo), sinon l'analyse échoue. Je suis sous l'impression que le lourd contexte de la dépendance est assez commun en C++.if
vsif constexpr
n'est pas sensible au contexte, car raconter l'une de l'autre se fait facilement en fonction de la séquence de jetons. Pour le jeton de la séquence, un identificateur est juste un identifiant. Si la nature de l'identifiant détermine comment analyser la séquence de jetons, alors vous avez affaire à quelque chose qui est problématique dans l'analyseur. Pas impossible, un peu "sensible au contexte". À l'aide d'un mot-clé, comme ce n'est pas sensible au contexte, à cet égard, car l'endroit où vous utilisez ces mots-clés, définit le contexte.requires
apparaît après un<>
ensemble d'arguments de modèle ou d'après un paramètre de la fonction liste, puis c'est une nécessite de la clause. Sirequires
apparaît lorsqu'une expression est valide, c'est une nécessite d'expression. Ceci peut être déterminé par la structure de l'arbre d'analyse, pas le table des matières de l'arbre d'analyse (les détails de la façon dont l'identificateur est défini serait le contenu de l'arborescence).concept
etrequires
. L'introduction d'un tiers, lorsque le second serait en mesure de couvrir les deux cas, sans grammaire et peu de l'utilisateur confronté à des problèmes, est juste un gaspillage. Après tout, le seul problème visuel, c'est que le mot-clé qui arrive à être répétée deux fois.La situation est exactement analogue à
noexcept(noexcept(...))
. Bien sûr, cela sonne plus comme une mauvaise chose que d'une bonne chose, mais laissez-moi vous expliquer. 🙂 Nous allons commencer avec ce que vous savez déjà:C++11 a "
noexcept
-clauses" et "noexcept
-expressions." Ils font des choses différentes.Un
noexcept
-clause dit, "Cette fonction devrait être noexcept quand... (une condition)." Il va sur une déclaration de fonction, prend un paramètre booléen, et provoque un changement de comportement dans la fonction déclarée.Un
noexcept
-expression dit, "Compilateur, veuillez me dire si (une expression) est noexcept." Il est lui-même une expression booléenne. Il n'a pas de "effets secondaires" sur le comportement du programme — il est juste de demander au compilateur de la réponse à une question oui/non. "Est-ce que cette expression noexcept?"Nous peut nest un
noexcept
-expression à l'intérieur d'unnoexcept
de la clause, mais en général, nous pouvons considérer cela comme de style de le faire.Il est considéré comme meilleur style pour encapsuler le
noexcept
-expression dans un type de trait.Le C++2a Projet de Travail a "
requires
-clauses" et "requires
-expressions." Ils font des choses différentes.Un
requires
-clause dit, "Cette fonction devraient participer à la résolution de surcharge lors de la... (une condition)." Il va sur une déclaration de fonction, prend un paramètre booléen, et provoque un changement de comportement dans la fonction déclarée.Un
requires
-expression dit, "Compilateur, veuillez me dire si (un ensemble d'expressions) est bien formé." Il est lui-même une expression booléenne. Il n'a pas de "effets secondaires" sur le comportement du programme — il est juste de demander au compilateur de la réponse à une question oui/non. "Est-ce que l'expression bien formée?"Nous peut nest un
requires
-expression à l'intérieur d'unrequires
de la clause, mais en général, nous pouvons considérer cela comme de style de le faire.Il est considéré comme meilleur style pour encapsuler le
requires
-expression dans un type de trait......ou en C++(C ++ 2a Projet de Travail) concept.
noexcept
a le problème quenoexcept(f())
pourrait dire soit interpréterf()
comme une valeur booléenne que nous utilisons pour définir la spécification ou vérifier si oui ou nonf()
estnoexcept
.requires
n'a pas cette ambiguïté, car les expressions de la vérification de la validité d'ores et déjà être introduit avec{}
s. Après, l'argument est fondamentalement "la grammaire dit."{}
sont facultatifs.{}
ne sont pas une option, ce n'est pas ce que ce commentaire montre. Cependant, c'est un grand commentaire démontrant l'analyse de l'ambiguïté. Serait probablement accepter que les commentaires (avec quelques explications) comme une réponse uniquerequires is_nothrow_incrable_v<T>;
devrait êtrerequires is_incrable_v<T>;
Je pense que cppreference concepts page explique cette. Je peux expliquer avec des "mathématiques" pour ainsi dire, pourquoi ce doit être comme ceci:
Si vous souhaitez définir un concept, ce faire, vous:
Si vous voulez déclarer une fonction qui utilise ce concept, vous le faites:
Maintenant, si vous ne voulez pas définir le concept en particulier, je suppose que tout ce que vous avez à faire est de substitution. Prendre cette partie
requires (T x) { x + x; };
et remplacer leAddable<T>
partie, et vous obtiendrez:qui est ce que vous demandez sur.
template<typename T> requires (T x) { x + x; }
et nécessitent qui nécessitent peut être à la fois la clause et de l'expression?requires
en tant que contrainte clause n'est pas à être unrequires
-expression. C'est simplement une utilisation possible de l'informatique.requires requires
. Ils pourraient avoir quelque chose d'ajouté à la grammaire, afin de permettretemplate<typename T> requires (T x) { x + x; }
mais ils n'ont pas. Barry veut savoir pourquoi ils n'ont pastemplate<class T> void f(T) requires requires(T (x)) { (void)x; };
signifie quelque chose de différent si vous supprimez l'un de l'requires
es.template template
paramètre là, tout en démontrantrequires requires
J'ai trouvé un commentaire de Andrew Sutton (l'un des Concepts les auteurs, qui l'ont mis en œuvre dans gcc) pour être tout à fait utile à cet égard, j'ai donc pensé que je venais de citer ici dans sa quasi-totalité:
Parce que vous dites qu'une chose a une exigence de B, et l'exigence de B a une exigence C.
La chose A dépend de B, qui à son tour nécessite C.
La "exige" la clause elle-même exige quelque chose.
Vous avez Une chose (nécessitant B (nécessitant C)).
Meh. 🙂
requires
ne sont pas conceptuellement la même chose (l'une est une clause, une sur-expression). En fait, si je comprends bien, les deux ensembles de()
dansrequires (requires (T x) { x + x; })
ont des significations très différentes (l'extérieur étant facultatif et contenant toujours un booléen constexpr; l'être intérieur d'une partie obligatoire de l'introduction d'une nécessite d'expression et de pas permettant réelle des expressions).