L'un des angles des concepts C ++ 20 est qu'il existe certaines situations dans lesquelles vous devez écrire requires requires
. Par exemple, cet exemple de [expr.prim.req] / 3 :
Une expression require peut également être utilisée dans une clause requires ([temp]) comme moyen d'écrire des contraintes ad hoc sur des arguments de modèle tels que celui ci-dessous:
template<typename T> requires requires (T x) { x + x; } T add(T a, T b) { return a + b; }
Le premier requiert introduit la clause requires , et le second introduit l' expression require .
Quelle est la raison technique derrière le besoin de ce deuxième requires
mot-clé? Pourquoi ne pouvons-nous pas simplement autoriser l'écriture:
template<typename T>
requires (T x) { x + x; }
T add(T a, T b) { return a + b; }
(Remarque: veuillez ne pas répondre à la grammaire requires
)
c++
c++-concepts
c++20
Barry
la source
la source
noexcept(noexcept(...))
.requires
sont des homonymes à mon avis: ils se ressemblent, épellent, sentent la même chose, mais sont intrinsèquement différents. Si je devais suggérer une solution, je suggérerais de renommer l'un d'entre eux.co_requires
? (Désolé, je n'ai pas pu résister).long long
.Réponses:
C'est parce que la grammaire l'exige. Cela fait.
Une
requires
contrainte n'a pas besoin d' utiliser unerequires
expression. Il peut utiliser n'importe quelle expression constante booléenne plus ou moins arbitraire. Par conséquent,requires (foo)
doit être unerequires
contrainte légitime .Une
requires
expression (cette chose qui teste si certaines choses suivent certaines contraintes) est une construction distincte; il est simplement introduit par le même mot-clé.requires (foo f)
serait le début d'unerequires
expression valide .Ce que vous voulez, c'est que si vous utilisez
requires
dans un endroit qui accepte les contraintes, vous devriez être capable de faire une «contrainte + expression» à partir de larequires
clause.Alors, voici la question: si vous placez
requires (foo)
dans un endroit qui convient pour une contrainte requiert ... jusqu'où l'analyseur doit-il aller avant de se rendre compte qu'il s'agit d'une contrainte requise plutôt que d'une contrainte + expression comme vous le souhaitez être?Considère ceci:
Si
foo
est un type, alors(foo)
est une liste de paramètres d'une expression requise, et tout ce qui se trouve dans le{}
n'est pas le corps de la fonction mais le corps de cetterequires
expression. Sinon,foo
est une expression dans unerequires
clause.Eh bien, vous pourriez dire que le compilateur devrait simplement comprendre ce qui
foo
est en premier. Mais C ++ n'aime vraiment pas quand l'acte de base d'analyse d'une séquence de jetons nécessite que le compilateur comprenne ce que ces identificateurs signifient avant de pouvoir donner un sens aux jetons. Oui, C ++ est sensible au contexte, donc cela se produit. Mais le comité préfère l'éviter dans la mesure du possible.Alors oui, c'est de la grammaire.
la source
requires
apparaît après un<>
ensemble d'arguments de modèle ou après une liste de paramètres de fonction, alors c'est une clause requires. Sirequires
apparaît là où une expression est valide, alors il s'agit d'une expression requise. Cela peut être déterminé par la structure de l'arborescence d'analyse, pas par le contenu de l'arborescence d'analyse (les spécificités de la manière dont un identificateur est défini seraient le contenu de l'arbre).concept
etrequires
. Introduire un troisième, alors que le second serait capable de couvrir les deux cas sans problèmes grammaticaux et peu de problèmes rencontrés par l'utilisateur, est juste un gaspillage. Après tout, le seul problème visuel est que le mot-clé se répète deux fois.La situation est exactement analogue à
noexcept(noexcept(...))
. Bien sûr, cela ressemble plus à une mauvaise chose qu'à une bonne chose, mais laissez-moi vous expliquer. :) Nous allons commencer par ce que vous savez déjà:C ++ 11 a des "
noexcept
-clauses" et des "-expressions"noexcept
. Ils font des choses différentes.Une
noexcept
-clause dit: "Cette fonction devrait être no sauf quand ... (une condition)." Il poursuit une déclaration de fonction, prend un paramètre booléen et provoque un changement de comportement dans la fonction déclarée.Une
noexcept
-expression dit: "Compilateur, veuillez me dire si (une expression) est noexcept." C'est en soi une expression booléenne. Il n'a aucun "effet secondaire" sur le comportement du programme - il demande simplement au compilateur la réponse à une question oui / non. «Est-ce que cette expression est nulle?Nous pouvons imbriquer une
noexcept
-expression dans unenoexcept
-clause, mais nous considérons généralement que c'est un mauvais style de le faire.Il est considéré comme un meilleur style d'encapsuler l'
noexcept
expression-dans un trait de type.Le brouillon de travail C ++ 2a contient des «
requires
-clauses» et des «-expressions»requires
. Ils font des choses différentes.Une
requires
-clause dit: "Cette fonction devrait participer à la résolution des surcharges quand ... (certaines conditions)." Il poursuit une déclaration de fonction, prend un paramètre booléen et provoque un changement de comportement dans la fonction déclarée.Une
requires
expression-indique: "Compilateur, veuillez me dire si (un ensemble d'expressions) est bien formé." C'est en soi une expression booléenne. Il n'a aucun "effet secondaire" sur le comportement du programme - il demande simplement au compilateur la réponse à une question oui / non. "Cette expression est-elle bien formée?"Nous pouvons imbriquer une
requires
-expression dans unerequires
-clause, mais nous considérons généralement que c'est un mauvais style de le faire.Il est considéré comme meilleur style d'encapsuler l'
requires
expression-dans un trait de type ...... ou dans un concept (C ++ 2a Working Draft).
la source
noexcept
a le problème quinoexcept(f())
pourrait signifier soit interpréterf()
comme un booléen 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 dont il vérifie la validité doivent déjà être introduites avec{}
s. Après cela, l'argument est essentiellement «la grammaire le dit».{}
soient facultatifs.{}
ne sont pas facultatifs, ce n'est pas ce que montre ce commentaire. Cependant, c'est un excellent commentaire démontrant l'ambiguïté de l'analyse. Accepterait probablement ce commentaire (avec quelques explications) comme une réponse autonomerequires is_nothrow_incrable_v<T>;
devrait êtrerequires is_incrable_v<T>;
Je pense que la page de concepts de cppreference explique cela. Je peux expliquer avec "maths" pour ainsi dire, pourquoi cela doit être comme ça:
Si vous souhaitez définir un concept, procédez comme suit:
Si vous souhaitez déclarer une fonction qui utilise ce concept, procédez comme suit:
Maintenant, si vous ne voulez pas définir le concept séparément, je suppose que tout ce que vous avez à faire est une substitution. Prenez cette pièce
requires (T x) { x + x; };
et remplacez laAddable<T>
pièce, et vous obtiendrez:c'est ce que vous demandez.
la source
template<typename T> requires (T x) { x + x; }
et exiger que require puisse être à la fois la clause et l'expression?requires
clause -as-constraint n'a pas besoin d' être unerequires
-expression. C'est simplement une utilisation possible.requires requires
. Ils auraient pu ajouter quelque chose à la grammaire pour autorisertemplate<typename T> requires (T x) { x + x; }
mais ils ne l'ont pas fait. Barry veut savoir pourquoi ils ne l'ont pas faittemplate<class T> void f(T) requires requires(T (x)) { (void)x; };
signifie quelque chose de différent si vous supprimez l'un desrequires
es.J'ai trouvé un commentaire d'Andrew Sutton (l'un des auteurs de Concepts, qui l'a implémenté dans gcc) très utile à cet égard, alors j'ai pensé que je le citerais ici dans sa quasi-totalité:
la source
Parce que vous dites qu'une chose A a une exigence B, et l'exigence B a une exigence C.
La chose A nécessite B qui à son tour nécessite C.
La clause «requiert» elle-même requiert quelque chose.
Vous avez la chose A (nécessitant B (nécessitant C)).
Meh. :)
la source
requires
ne sont pas conceptuellement la même chose (l'une est une clause, l'autre une expression). En fait, si je comprends bien, les deux ensembles de()
inrequires (requires (T x) { x + x; })
ont des significations très différentes (l'extérieur étant facultatif et contenant toujours une constexpr booléenne; l'intérieur étant une partie obligatoire de l'introduction d'une expression requiert et ne permettant pas d' expressions réelles).