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,


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;
}

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.


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;
}

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


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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.