0
votes

Les concepts imposent que le 2ème argument soit de type int

Comme le titre l'indique, je veux que foo n'accepte que le type int car c'est le deuxième paramètre.

foo(1, "TTT");

Cela fonctionne mais g ++ (version 10.2.0) génère cet avertissement

template <typename U, typename T>  concept IS_INT =
requires (U u, T t) {
        std::same_as<T, int>;
};

Autre façon d'écrire IS_INT {U, T} ? se débarrasser de cet avertissement?


Enfin, comment réécrire cette contrainte (t est int) sans avoir à ajouter 0?

    { t + 0 } -> std::same_as<int>;

MODIFIER :

test.cpp:11:8: warning: template-introductions are not part of C++20 concepts [-fconcepts-ts]
   11 | IS_INT {U, T}
      | ~~~~~~~^~~~~~

Ne donne pas d'erreur de compilation pour

#include <iostream>
#include <concepts>
#include <type_traits>

template <typename U, typename T>  concept IS_INT =
requires (U u, T t) {
        { t + 0 } -> std::same_as<int>;
};


IS_INT {U, T}
void foo (U u, T t)
{
        std::cout << "IS INT!" << std::endl;
        return;
}


int main()
{
        foo(1, 1);
        return 0;
}


4 commentaires

IS_INT {U, T} Quelle est cette syntaxe? { t + 0 } Vous voulez que le type promu soit int , ou le type int ?


@KamilCuk en.cppreference.com/w/cpp/experimental/constraints


Ensuite, vous voulez que std::same_as<T, int> , pas t + 0 soit int . + opérateur fera la promotion de ses opérandes.


@KamilCuk oui, mais si je change le corps { t + 0 } -> std::same_as<int>; à std::same_as<T, int>; dans main pour foo(1, "TTT") je n'obtiens pas d'erreur de compilation. Je suppose que j'utilise mal les concepts ...


3 Réponses :


2
votes

Vous pouvez conserver la forme binaire même dans la version actuelle des concepts de base:

template<class T, class U> concept is_int = std::same_as<T, int>;

(notez l'ordre inversé des paramètres: en règle générale, si un concept accepte plusieurs paramètres, seul le premier est spécial et U vient en premier dans votre définition). Le sucre TS complet, comme l'avertit g ++, n'est actuellement pas standard.

Enfin, comment réécrire cette contrainte (t est int) sans avoir à ajouter 0?

template <typename U, typename T>  concept IS_INT =
requires (U u, T t) {
        { t + 0 } -> std::same_as<int>;
};


template<class T, IS_INT<T> U>
void foo (U u, T t)
{
        std::cout << "IS INT!" << std::endl;
        return;
}

?


3 commentaires

Oh, en changeant { t + 0 } -> std::same_as<int>; à std::same_as<T, int>; ce simple ... et template<class T, IS_INT<T> U> est ce dont j'avais besoin. Merci!


Je viens de le remarquer en changeant { t + 0 } -> std::same_as<int>; à std::same_as<T, int>; ne donnera pas d'erreur pour foo(1, "TTT") alors que { t + 0 } -> std::same_as<int>; Est-ce que. Est-ce que je manque quelque chose?


@TonyTannous oui. L'une est une exigence imbriquée qui doit être satisfaite, l'autre n'est qu'une expression unique qui compile dans tous les cas et ne contraint vraiment rien.



-1
votes

Pourquoi le code est-il trop compliqué pour son objectif? Le code, dans sa forme la plus simple, ressemblerait à:

#include <iostream>
#include <concepts>
#include <type_traits>

template <typename U, typename T>  concept IS_INT =
        std::is_integral<T>::value;


IS_INT {U, T}
void foo (U u, T t)
{
        std::cout << "IS INT!" << std::endl;
        return;
}


int main()
{
        foo(1, 1);
        //foo(1,1.0)    // Wouldn't work for T as double - just as expected.
        return 0;
}

La raison pour laquelle les «concepts de contrainte» ont été introduits dans C ++ 20 est de supprimer l'encombrement que SFINAE imposerait autrement. Pour supprimer l'avertissement, ajoutez simplement l' -fconcepts-ts .


2 commentaires

Ce n'est pas une syntaxe C ++ 20 valide.


Il a supprimé l'encombrement du code d'origine plutôt que d'avoir à promouvoir en int avec l'opérateur +. Bien que ne spécialisant pas les paramètres du modèle, la réponse s'aligne davantage sur le code d'origine - et donc ma réponse comme ci-dessus. Cependant, je comprends que la question est étiquetée c ++ 20



2
votes

Comme le titre l'indique, je veux que foo n'accepte que le type int comme deuxième paramètre.

Cela demande un concept unaire - vous voulez contraindre le 2ème paramètre, par lui-même, à être int . Il s'agit de vérifier une exigence d'un seul type.

Ce:

{ t + 0 } -> std::same_as<int>;

est un concept binaire (indépendamment de ce qui suit le = ). Cela contraint deux types différents, T et U , d'une certaine manière. Peu importe ce que vous écrivez après cela, cela ne répond pas réellement à votre cas d'utilisation.

Ce que vous voulez dire, c'est qu'un type est un int . C'est:

template <typename U, std::same_as<int> T>
void foo (U u, T t);

Que vous pouvez utiliser, ainsi:

template <typename U, is_int T>
void foo (U u, T t);

Cela n'a aucune contrainte sur le premier paramètre et le deuxième paramètre doit être de type int .


Maintenant, la bibliothèque standard est livrée avec un concept pour exactement ce problème. Il s'appelle same_as :

template <typename T> concept is_int = std::is_same_v<T, int>;

En outre, il convient de noter l'exigence:

template <typename U, typename T>  concept IS_INT =

N'exige pas en fait que t ait le type int . Cela vaut également pour tout type entier inférieur à int , en raison de la promotion d'entiers.


1 commentaires

Merci pour la réponse (déjà +1) Je suppose que je ne sais pas quand utiliser le requiert et imposer des contraintes dans le bloc {} . Merci encore.