Surcharger une fonction à l'aide de modèles

34

J'essaie de définir une fonction à l'aide de modèles et je veux que le nom de type soit int ou anEnum (une énumération spécifique que j'avais définie). J'ai essayé ce qui suit mais j'ai échoué:

template <int | anEnum T> // or <int T, anEnum T> or <int, anEnum T>
bool isFunction(const T &aVariable){}

Ce que j'essaie de faire, c'est d'utiliser des modèles, au lieu de définir deux fonctions surchargées. Je préfère que la fonction soit appelée comme suit, sans que le programmeur doive considérer le type

isFunction(aVariable) // and not isFunction<int> (aVariable) nor isFunction<anEnum> (aVariable)

Fondamentalement, je veux que cette fonction soit basée sur des modèles pour les types int et aNum. J'ai cherché cela, mais je n'ai pas pu trouver la réponse. Que puis-je manquer? Merci,

bg
la source
S'il s'agit exactement d'une seule énumération ou du type int, pourquoi ne pas simplement écrire les deux fonctions? Pourquoi avez-vous besoin d'un modèle dans ce cas?
Klaus
Et les autres types? Voulez-vous revenir falsepour d'autres types ou ne pas instancier la fonction pour d'autres types.
frogatto
@frogatto Non, la valeur de retour bool n'a rien avec les types.
bg
@Klaus, j'ai demandé à apprendre des alternatives. Sur la base des réponses actuelles, j'ai décidé de simplement définir les deux fonctions.
bg

Réponses:

25

En plus de la réponse non C ++ 20, si vous êtes par hasard en mesure d'utiliser C ++ 20 et ses conceptsfonctionnalités, je vous suggère l'implémentation suivante:

#include <iostream>
#include <concepts>

enum class MyEnum {
    A,
    B,
    C
};

template <typename T>
concept IntegralOrEnum = std::same_as<MyEnum, T> || std::integral<T>;

template <IntegralOrEnum T>
bool isFunction(T const& aVariable) {
    return true;
}

int main() {
    isFunction(MyEnum::A);
    isFunction(3);
    isFunction("my_string"); // error
    return 0;
}

Démo

MISE À JOUR

Selon le commentaire de @RichardSmith , voici une approche plus évolutive et réutilisable:

template <typename T, typename ...U>
concept one_of = (std::is_same_v<T, U> || ...);

template <one_of<int, MyEnum> T>
bool isFunction(T const& aVariable) {
    return true;
}
Casse Noisette
la source
Pour le cas spécifique d'exiger que le type soit l'un des deux types spécifiques, quelque chose comme ça pourrait mieux fonctionner:template<typename T, typename ...U> concept one_of = (std::is_same_v<T, U> || ...); template<one_of<int, MyEnum> T> bool isFunction(T const& aVariable) {
Richard Smith
1
@RichardSmith J'ai également mis à jour ma réponse avec cela. Je trouve cela plus réutilisable et évolutif. Merci
NutCracker
21

Il y a deux façons d'y parvenir. Tous impliquent l'utilisation de l'en- type_traitstête. Vous pouvez par exemple affirmer statiquement les types en question dans le corps de la fonction.

Ou, si vous devez considérer cette fonction parmi d'autres surcharges, une technique SFINAE peut être utilisée.

template<typename T>
auto isFunction(const T &aVariable) 
  -> std::enable_if_t<std::is_same<T, int>::value || std::is_same<T,anEnum>::value, bool> {
}

Cela supprimera la fonction d'un ensemble de surcharge avant son appel si les types ne correspondent pas. Mais si vous n'avez pas besoin de ce comportement, une assertion statique permet un message d'erreur plus convivial pour le programmeur.

Conteur - Unslander Monica
la source
3

Et cette solution? Un code avec la fonction sera compilé si le type T satisfait vos exigences. Sinon, l'assertion statique a échoué.

#include <type_traits>
enum anEnum {
    //
};

template <typename T, bool defined = std::is_same<T, int>::value ||
                                     std::is_same<T, anEnum>::value>
bool isFunction(const T& aVariable)
{
    static_assert(defined, "Invalid specialization");

    bool result = false;
    // Put your code here
    return result;
}
ixjxk
la source
1
Cela ne fonctionne pas bien avec la résolution de surcharge s'il y a d'autres signatures présentes (par exemple, une hypothétique isFunction(std::string_view)). La signature sera toujours une correspondance valide, mais l'instanciation provoque une erreur.
LF
Vous pouvez déclarer les signatures inutiles supprimées: bool isFunction (std :: string_view) = delete;
ixjxk
Je parle de surcharges supplémentaires. Dans ce cas, cette signature non valide peut finir par être une correspondance exacte (par exemple, pour les littéraux de chaîne), bloquant ainsi la surcharge.
LF
0

J'ai amélioré la réponse https://stackoverflow.com/a/60271100/12894563 . 'Si constexpr' peut aider dans cette situation:

template <typename T>
struct always_false : std::false_type {};

template <typename T>
bool isFunction(const T& aVariable)
{
    if constexpr(std::is_same_v<T, int> || std::is_same_v<T, anEnum>)
    {
        std::cout << "int\n";
        // put your code here
        return true;
    }
    else
    {
        static_assert(always_false<T>::value, "You should declare non-template function or write if constexpr branch for your type");
        return false;
    }
}

bool isFunction(std::string_view)
{
    std::cout << "std::string_view\n";
    return true;
}

int main()
{
    isFunction(std::string_view("1L"));
    isFunction(1);
    //isFunction(1L); // will produce an error message from static_assert
}

isFunction (1L) échouera car il n'y a pas de fonction surchargée ou de branche 'if constexpr'.

MISE À JOUR: Correction manquée

template <typename T>
struct always_false : std::false_type {};

https://godbolt.org/z/eh4pVn

ixjxk
la source
static_assert(false, ...)est NDR mal formé, sans même être utilisé. Si vous avez de la chance, votre compilateur vous le dira tout de suite, comme Clang le fait, godbolt.org/z/m_Gk9n
StoryTeller - Unslander Monica
Merci beaucoup pour ton commentaire, j'ai fait une erreur. Corrigé, godbolt.org/z/eh4pVn
ixjxk