34 votes

Comment puis-je forcer le type de paramètre de modèle à être signé?

Je vais utiliser l'exemple suivant pour illustrer ma question:

 template<typename T>
T diff(T a, T b)
{
  return a-b;
}
 

J'espère que cette fonction de modèle ne fonctionne que lorsque le type T est signé. La seule solution que je puisse comprendre consiste à 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?

45voto

alexeykuzmin0 Points 692

Vous pouvez utiliser std::is_signed 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, envisager 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 un vide struct en cas Test est faux et qu'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 les non signés, ce n'est pas définie et le compilateur utilise SFINAE règle, donc, si vous avez besoin de spécifier une mise en œuvre pour un particulier non-signé, vous pouvez facilement le faire:

template<>
unsigned diff(unsigned, unsigned)
{
    return 0u;
}

Quelques liens pertinents: enable_if, is_signed.

36voto

Louen Points 336

Qu'en est-il de 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 ici: http://ideone.com/l8nWYQ

11voto

mister why Points 829

Je voudrais utiliser static_assert avec un joli message d'erreur. enable_if obtenez seulement votre IDE en difficulté et ne parviennent pas à compiler avec un message du genre

identificateur diff non trouvé

ce qui ne l'aide pas beaucoup.

Alors pourquoi ne pas, comme ceci:

#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 genre de message:

erreur: T doit être signé

avec l'emplacement et les valeurs de l'appel à l' diff , et c'est exactement ce que vous cherchez.

7voto

Edgar Rokyan Points 1335

Comme autre option, vous pouvez probablement ajouter static_assert avec std :: is_signed trait trait:

 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
 

2voto

Yakk Points 31636

Il y a donc quelques problèmes que j'ai avec votre fonction.

Tout d'abord, votre fonction exige que tous les 3 types de match -- la gauche, la droite et les types de résultats. Donc, signed char a; int b; diff(a-b); de ne pas travailler pour aucune bonne raison.

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 veux faire est de faire une diff objet; vous ne pouvez pas facilement passer votre diff fonction autour de, et des fonctions d'ordre supérieur sont impressionnantes.

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{} d'un algorithme, puisqu'il détient la "surcharge set" diff dans un (trivial) objet C++.

Maintenant c'est du sérieux exagéré. Un simple static_assert peut également fonctionner.

L' static_assert va générer de meilleurs messages d'erreur, mais ne prend pas en charge les autres code à l'aide de SFINAE pour voir si diff peut être appelé. Il suffit de générer une erreur de disque dur.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X