2017-09-01 3 views
1

Je peux utiliser des modèles et supprimer des fonctions pour empêcher l'appel de factoriel avec des variables de type caractère ou flottant comme suit. Comment écrire la fonction de suppression pour les factoriels avec des arguments négatifs?Utilisation de delete pour empêcher les appels avec des valeurs non valides en C++ 14

template <typename T> 
constexpr T factorial(T n) 
{ 
    return (n == 1 || n == 0) ? 1 : (n * factorial(n - 1)); 
} 

constexpr float factorial(double) = delete; 
constexpr char factorial(char) = delete; 

int main() 
{ 
    constexpr auto fiveFactorial = factorial(5); 
    constexpr auto point5fact = factorial(0.5); // Error. Call to deleted version 
    constexpr auto letter5fact = factorial('5'); // DITTO 
    constexpr auto minusFact = factorial(-1); // How to prevent this using delete? 
} 
+2

Vous ne pouvez pas, puisque la valeur est évaluée * runtime *. C'est correct avec les types, puisqu'ils sont connus au moment de la compilation, mais vous ne pouvez pas obtenir d'erreurs de compilation pour des choses qui sont seulement connues à l'exécution. La seule solution que je peux voir est de s'assurer que 'T' est un type * unsigned *. –

+0

Une autre solution possible, si vous voulez seulement autoriser les types entiers non signés, est de * déclarer * la fonction générique mais de ne pas l'implémenter (ou peut-être même la marquer comme effacée?). Ensuite, spécialisez la fonction pour les types entiers non signés avec une définition (implémentation). –

Répondre

4

Impossible. = delete est une chose à la compilation, alors que vos arguments ne sont pas toujours connus à la compilation.

Vous pouvez utiliser le paramètre unsigned à la place et supprimer toutes ces surcharges supprimées, au prix d'être incapable d'appeler votre fonction avec des nombres signés, comme factorial(2).

template <typename T> constexpr T factorial(T n) 
{ 
    static_assert(std::is_unsigned_v<T> && !std::is_same_v<T, char>, 
        "Parameter type must be integral, unsigned, and not `char`."); 
    return (n == 1 || n == 0) ? 1 : (n * factorial(T(n - 1))); 
} 
+0

Le 'factorial (5)' de l'OP ne fonctionnera plus avec ceci, bien sûr. Si le PO est d'accord avec ça, d'accord, mais je pense que ça pourrait être utile. – hvd

+0

@hvd Merci, édité. J'ai juste oublié ça. – HolyBlackCat

+0

Mais maintenant l'implémentation doit être adaptée: l'appel récursif doit être factoriel (n - 1u), sinon l'appel 'factorial (5us)' (appel avec une valeur courte non signée 5) échouera, car '5us - 1 'est de type' int', qui n'est pas non signé. –

1

Comment écrire la fonction de suppression pour factorielles avec des arguments négatifs?

Vous ne pouvez pas, puisque la valeur de l'argument est une propriété d'exécution, mais vous pouvez uniquement supprimer les surcharges en fonction des types.

Mais puisqu'il s'agit de constexpr, il existe une autre méthode: vous ne faites que rendre la fonction mal formée pour les entrées négatives dans une expression constante. Par exemple, en lançant. En outre, puisque vous avez étiqueté ce C++ 14, je change votre implémentation en boucle:

template <typename T> 
constexpr T factorial(T n) 
{ 
    if (n < 0) throw std::runtime_error("bad user!"); 

    T product = 1; 
    for (T i = 2; i <= n; ++i) { 
     product *= i; 
    } 
    return product; 
} 

constexpr auto good = factorial(5); // fine 
constexpr auto bad = factorial(-1); // compile error because factorial(-1) 
            // is not a valid constant expression