8
votes

Paramètres de modèle non-type et nécessite

J'apprends des concepts et je ne peux pas trouver un moyen de restreindre la valeur (et non le type) du paramètre de modèle non type.

Exemple de code qui se compile, bien que j'aurais souhaité que ce ne soit pas le cas (en raison d'un échec d'exigence):

#include <cassert>

enum Bla{
    Lol, 
    Haha
};

template<Bla b>
requires requires{
    // my guess is that this just checks that this is valid expression, not
    // that it is true
    b>1; 
}
void f(){
    assert(b>1);
}

int main() {
    f<Lol>(); // compiles, not funny ;)
}

Remarque: ceci est un exemple simplifié (je veux une "surcharge de modèle") donc static_assert n'est pas bon pour moi, et j'essaie d'éviter std::enable_if car la syntaxe est hideuse.


2 commentaires

static_assert ferait échouer, mais je suppose que ce n'est qu'un exemple simplifié


@ idclev463035818 Je le veux pour surcharge, donc oui.


3 Réponses :


5
votes

Puisque f doit être contraint que par la valeur du paramètre de modèle non-type, vous pouvez simplement écrire une clause requires au lieu d'une contrainte ad-hoc requires requires :

static_assert(b>1);

Voici une démo .

Vous n'avez besoin que d'une expression requires requires si vous souhaitez effectuer des vérifications plus compliquées sur le paramètre de modèle. Dans ce cas, je recommande quand même d'utiliser un concept nommé sur une contrainte ad hoc. Cela rend le code plus lisible et vous permet de réutiliser le concept à d'autres endroits.


Quant à l' assert , c'est une construction au moment de l'exécution, donc cela n'affecte en rien la compilation, en supposant que l'expression à l'intérieur de l' assert est syntaxiquement valide. Vous devez utiliser static_assert place, si vous souhaitez vérifier le paramètre du modèle au moment de la compilation:

template<Bla b>
requires (b>1) 
void f() {}


2 commentaires

@ idclev463035818 a raison, affirmer est juste pour montrer que le code meurt au moment de l'exécution ... Votre solution est excellente, mais maintenant je ne comprends pas pourquoi je n'ai besoin que d'un seul :), mais oui, cela sort du cadre de cette question.


@NoSenseEtAl requires requires une contrainte ad-hoc. Il est uniquement utilisé avec des expressions obligatoires, pas des clauses obligatoires. Ouais, ça devrait probablement être une question différente.



3
votes

Contraintes, requiert-clause: s et requires-expression: s

Vous devez faire la différence entre une clause requires et une expression requires .

// Allows any specialization over MyEnum.
template<MyEnum e>
struct Wrapped {};
    
// Overloading Wrapped specializations by 
// mutually exclusive constraints:
template<MyEnum e>
void f(Wrapped<e>) requires (e == MyEnum::Foo || e == MyEnum::Bar) {
    std::cout<< __PRETTY_FUNCTION__ << "\n";
}

template<MyEnum e>
void f(Wrapped<e>) requires (e == MyEnum::Baz) {
    std::cout<< __PRETTY_FUNCTION__ << "\n";
}

int main() {
    f(Wrapped<MyEnum::Foo>{}); // void f(Wrapped<e>) requires  e == MyEnum::Foo || e == MyEnum::Bar [with MyEnum e = MyEnum::Foo]
    f(Wrapped<MyEnum::Bar>{}); // void f(Wrapped<e>) requires  e == MyEnum::Foo || e == MyEnum::Bar [with MyEnum e = MyEnum::Bar]
    f(Wrapped<MyEnum::Baz>{}); // void f(Wrapped<e>) requires  e == MyEnum::Baz [with MyEnum e = MyEnum::Baz]
}

En particulier, selon [temp.pre] / 1 , la grammaire d'une clause requires est:

enum class MyEnum {
    Foo,
    Bar,
    Baz
};

// Allows any specialization over MyEnum.
template<MyEnum e>
struct Wrapped {};
    
// Allows only Wrapped objects of certain
// specializations.
template<MyEnum e>
void f(Wrapped<e>) requires (e == MyEnum::Foo || e == MyEnum::Bar) {
}

int main() {
    f(Wrapped<MyEnum::Foo>{}); // OK
    f(Wrapped<MyEnum::Bar>{}); // OK
    f(Wrapped<MyEnum::Baz>{}); // Error: ... constraints not satisfied
}

contrainte-ou-expression-logique , à son tour, est une expression-primaire , qui comprend une expression-requise : s .

Exemple appliqué à OP: s: contraindre un paramètre de modèle non type

Dans votre cas, vous utilisez une expression requise ad-hoc (par rapport à une contrainte nommée) comme condition requise pour une clause require . Cependant, pour votre cas d'utilisation, il suffit d'utiliser une clause requires avec une expression constante . En particulier, vous pouvez restreindre la valeur d'un paramètre de modèle non-type par une expression constante dans la clause requiert de fin d'une entité modèle donnée, par exemple un modèle de fonction:

requires-clause:
  requires constraint-logical-or-expression

Appliqué pour la surcharge avec des contraintes mutuellement exclusives:

template<Bla b>
void f() requires .... {}
//                ^^^^ - constant-expression OR
//                       requires-expression
//       
//       ^^^^^^^^^^^^^ - requires-clause 

0 commentaires

4
votes

Si vous n'avez qu'une condition booléenne et rien d'autre, procédez comme suit:

template<Bla b>
requires requires
{
    requires b > 1;
//  ^~~~~~~~
}
void f() {}

Une autre syntaxe plus longue, si vous avez besoin de vérifier plus de choses dans la même, requires -expression:

template<Bla b>
requires(b > 1)
void f() {}


5 commentaires

Le commentaire le plus voté sur cette question a trouvé une réponse? : P stackoverflow.com/questions/54200988/... Accepter cela car bien que la réponse cigen soit plus tôt, vous donnez un moyen de faire des choses plus compliquées ...


Pourquoi s'arrêter à imbriquer une seule expression-obligatoire dans une autre? ;)


@dfrib Vous venez de trouver un bogue GCC avec __PRETTY_FUNCTION__ . : P


@HolyBlackCat quel bug? manque de place?


@NoSenseEtAl Oui.