web-dev-qa-db-fra.com

Comment restreindre une classe de modèle à certains types intégrés?

Ce problème a été discuté plusieurs fois, mais toutes les solutions que j'ai trouvées n'ont pas fonctionné ou étaient basées sur l'assertion statique de boost. Mon problème est simple. J'ai une classe et je veux seulement autoriser les vrais types (double et float). Je veux une erreur de compilation si j'essaie d'instancier la classe avec un type autre que float ou double. J'utilise Visual C++ 11. Voici ce que j'ai essayé:

template <typename RealType>
class A
{
  // Warning C4346
  static_assert(std::is_same<RealType, double>::value || std::is_same<RealType, float>::value);
}


template <typename RealType>
class A
{
  // Error C2062: type 'unknown' unexpected
  static_assert(decltype(RealType) == double || decltype(RealType) == float);
}

Des idées? Merci d'avance!

27
quant

Une solution que j'ai vue consiste à utiliser std::enable_if dans un alias de type. Quelque chose comme:

using value_type = typename std::enable_if<
                    std::is_same<float, RealType>::value ||
                    std::is_same<double, RealType>::value,
                    RealType
                >::type;

value_type n'existe que si RealType est exactement float ou double. Sinon, le type n'est pas défini et la compilation échoue.

Je vous préviens cependant d'être trop strict avec les types. Les modèles sont aussi puissants qu'ils le sont en partie parce que la saisie de canard qu'ils font signifie que tout type qui peut être utilisé comme vous le souhaitez, fonctionnera. Interdire les types pour les interdire ne vous rapporte généralement pas grand-chose et peut rendre les choses moins flexibles qu'elles ne pourraient l'être. Par exemple, vous ne pourriez pas utiliser un type avec plus de précision, comme un type à grande décimale.

21
cHao

Dans votre premier exemple, static_assert devrait prendre un second paramètre qui serait un littéral de chaîne, sinon il est réputé échouer ( edit: la suppression du second paramètre est légale puisque C + +17). Et ce deuxième argument ne peut pas être utilisé par défaut.

Votre deuxième exemple est incorrect pour plusieurs raisons:

  • decltype est destiné à être utilisé sur une expression, pas sur un type.
  • Vous ne pouvez tout simplement pas comparer les types avec ==, la bonne façon de procéder est ce que vous essayez lors de votre première tentative avec std::is_same.

Donc, la bonne façon de faire ce que vous essayez de réaliser est:

#include <type_traits>

template <typename RealType>
class A
{
  static_assert(std::is_same<RealType, double>::value || std::is_same<RealType, float>::value,
                "some meaningful error message");
};

De plus, je parie que vous essayez de restreindre votre modèle à des valeurs à virgule flottante. Pour ce faire, vous pouvez utiliser le trait std::is_floating_point :

#include <type_traits>

template <typename RealType>
class A
{
  static_assert(std::is_floating_point<RealType>::value,
                "class A can only be instantiated with floating point types");
};

Et en bonus, prenez cet exemple en ligne .

29
Morwenn

De cette façon, il permet également une spécialisation pour différents types:

template<typename T, typename Enable = void>
class A {
/// Maybe no code here or static_assert(false, "Nice message");
};

/// This specialization is only enabled for double or float.
template<typename T>
class A<T, typename enable_if<is_same<T, double>::value || is_same<T, float>::value>::type> {

};
5
Juraj Blaho