Je vais utiliser l'exemple suivant pour illustrer ma question:
template<typename T>
T diff(T a, T b)
{
return a-b;
}
Je m'attends à ce que cette fonction de modèle ne fonctionne que lorsque le type T est signé. La seule solution que je peux trouver est d'utiliser le mot-clé delete pour tous les types non signés:
template<>
unsigned char diff(unsigned char,unsigned char) == delete;
template<>
unsigned char diff(unsigned char,unsigned char) == delete;
Y a-t-il d'autres solutions?
Vous pouvez utiliser std::is_signed
ensemble avec std::enable_if
:
template<typename T>
T diff(T a, T b);
template<typename T>
std::enable_if_t<std::is_signed<T>::value, T> diff(T a, T b) {
return a - b;
}
Ici std::is_signed<T>::value
est true
si et seulement si T
est signé (BTW, c'est aussi true
pour les types à virgule flottante, si vous n'en avez pas besoin, envisagez de combiner avec std::is_integral
).
std::enable_if_t<Test, Type>
est le même que std::enable_if<Test, Type>::type
. std::enable_if<Test, Type>
est défini comme une structure vide dans le cas où Test
est faux et comme une structure avec un seul typedef type
égal au paramètre de modèle Type
sinon.
Donc, pour les types signés, std::enable_if_t<std::is_signed<T>::value, T>
est égal à T
, tandis que pour non signé il n'est pas défini et que le compilateur utilise la règle SFINAE, donc, si vous devez spécifier une implémentation pour un type non signé particulier, vous pouvez facilement le faire:
template<>
unsigned diff(unsigned, unsigned)
{
return 0u;
}
Que diriez-vous static assert
avec std::is_signed
?
template<typename T>
T diff(T a, T b)
{
static_assert(std::is_signed<T>::value, "signed values only");
return a-b;
}
Voir en direct là-bas: http://ideone.com/l8nWYQ
J'utiliserais static_assert
avec un joli message d'erreur. enable_if
n'obtiendra que votre IDE en difficulté et ne pourra pas être compilé avec un message comme
identifiant
diff
introuvable
ce qui n'aide pas beaucoup.
Alors pourquoi ne pas aimer ça:
#include <type_traits>
template <typename T>
T diff(T a, T b)
{
static_assert(std::is_signed< T >::value, "T should be signed");
return a - b;
}
de cette façon, lorsque vous appelez diff
avec autre chose qu'un type signé, vous obtiendrez le compilateur pour écrire ce type de message:
erreur: T doit être signé
avec l'emplacement et les valeurs de l'appel à diff
et c'est exactement ce que vous recherchez.
Comme autre option, vous pouvez probablement ajouter static_assert avec std :: is_signed trait de type:
template<typename T>
auto diff(T x, T y)
{
static_assert(std::is_signed<T>::value, "Does not work for unsigned");
return x - y;
}
Pour que:
auto x = diff(4, 2); // works
auto x = diff(4U, 2U); // does not work
Qu'attend votre programme en conséquence? En l'état, vous retournez un non signé à la suite d'une différence. À mon humble avis, il s'agit d'un bug en attente de se produire.
#include <type_trait>
template<typename T>
auto diff(T&& a, T&& b)
{
static_assert (std::is_unsigned<T>::value);
return typename std::make_signed<T>::type(a - b);
}
Une attente plus moderne pour écrire ceci:
inline auto diff(const auto a, const auto b)
{
static_assert ( std::is_unsigned<decltype(a)>::value
&& std::is_unsigned<decltype(b)>::value );
return typename std::make_signed<decltype(a -b)>::type(a - b);
}
[edit] Je ressens le besoin d'ajouter ce commentaire: utiliser des types intégraux non signés dans les équations mathématiques est toujours délicat. L'exemple ci-dessus serait un complément très utile à n'importe quel package mathématique.Si des situations réelles, vous devez souvent recourir à la conversion pour faire le résultat des différences signed
, ou les mathématiques ne fonctionnent pas.
Il y a donc quelques problèmes que j'ai avec votre fonction.
Tout d'abord, votre fonction nécessite que les 3 types correspondent - les types gauche, droit et résultat. Donc, signed char a; int b; diff(a-b);
ne fonctionnera pas sans raison valable.
template<class L, class R>
auto diff( L l, R r )
-> typename std::enable_if<
std::is_signed<L>::value && std::is_signed<R>::value,
typename std::decay<decltype( l-r )>::type
>::type
{
return l-r;
}
la deuxième chose que je voudrais faire est de créer un objet diff; vous ne pouvez pas facilement passer votre fonction diff
et les fonctions d'ordre supérieur sont géniales.
struct diff_t {
template<class L, class R>
auto operator()(L l, R r)const
-> decltype( diff(l,r) )
{ return diff(l,r); }
};
Maintenant, nous pouvons passer diff_t{}
à un algorithme, car il contient "l'ensemble de surcharge" de diff
dans un objet C++ (trivial).
Maintenant, c'est une exagération grave. Un simple static_assert
peut également fonctionner.
Le static_assert
générera de meilleurs messages d'erreur, mais ne prendra pas en charge d'autres codes utilisant SFINAE pour voir si diff
peut être appelé. Cela générera simplement une erreur matérielle.