12
votes

Une classe de trait is_functor C ++ est-elle possible?

Comment puis-je déduire statiquement si un argument est un objet de fonction C ++ (foncteur)?

template <typename F>
void test(F f) {}


6 commentaires

Que diriez-vous de is_function :: valeur ?


groups.google.com/group/comp.lang.c++. modéré / msg / ... pourrait vous intéresser.


Voulez-vous simplement tester des foncteurs ou pour un objet appelable? Cela semble être une utilisation sfinae du trait résultat_of fonctionnerait pour identifier tout type appelable. Je suis un peu surpris qu'il ne semble pas y avoir de std :: is_ is_callable un trait déjà.


@ Bames53: J'ai consulté résultat_of de cette manière toute la journée. Il semble qu'il y ait de nombreuses situations dans lesquelles GCC produira une erreur, dans une surcharge, bien que une autre surcharge valide existe.


@Fitkit: Cela ne compile pas, et is_function :: valeur est également rejeté: argument de modèle non valide .


Il n'y a pas de telle chose que f :: opérateur () . Toutes les fonctions membres ont une liste d'arguments, même si cette liste est vide. Êtes-vous à la place à la recherche de l'existence de f :: opérateur () () ? Oui, c'est possible de détecter.


4 Réponses :


0
votes
template<typename T>
typename std::enable_if<is_functor<T, void(T::*)()>::value>::type func()
{
}

3 commentaires

Typename DeclType ? Et votre solution ne fonctionne pas si l'opérateur () est surchargé.


Votre définition du fonctionnement est incomplète. Un foncteur standard est un pointeur de fonction ou un objet avec opérateur surchargé () .


J'ai posté une solution différente; @Maximyegoruskkin mais le nouveau ne change pas à ce sujet, hmmm



12
votes

Il est possible de créer un tel trait, avec deux restrictions:

  1. Pour le compilateur, une fonction libre est quelque chose de fondamentalement différent d'un fonctionnement de classe qui surcharge opérateur () code>. Nous devons donc traiter les deux cas séparément lors de la mise en œuvre. Ce n'est pas un problème pour l'utilisation, nous pouvons cacher ce détail de mise en œuvre de l'utilisateur. LI>
  2. Nous devons connaître la signature de la fonction que vous souhaitez appeler. Ce n'est généralement pas un problème, et il a l'effet secondaire nice que notre trait est capable de gérer des surcharges de manière assez nativement. Li> ol>

    étape 1: fonctions libres strong> p>

    Commençons par des fonctions libres, car elles sont difficiles à détecter. Notre tâche est, lorsqu'elle est donnée un pointeur de fonction, pour déterminer si la signature de ce pointeur de fonction correspond à la signature transmise en tant que deuxième argument de modèle. Pour pouvoir comparer ceux-ci, nous devons soit obtenir une compréhension de la signature de fonction sous-jacente, soit créer un pointeur de fonction de notre signature. J'ai choisi arbitrairement ce dernier: p> xxx pré>

    maintenant tout ce qui reste à faire est de comparer et nous sommes terminés avec la partie de fonction libre: p> xxx Pré>

    Étape deux: foncteurs de classe strong> p>

    Celui-ci est un peu plus impliqué. Nous pourrions facilement détecter avec Sfinae si une classe définit un opérateur () code>: p> xxx pré>

    mais qui ne nous dit pas si on ne nous dit pas si on ne nous dit pas si l'on existe pour la fonction souhaitée Signature! Heureusement, nous pouvons utiliser un tour ici: les pointeurs sont des paramètres de modèle valides. Ainsi, nous pouvons d'abord utiliser le pointeur de la fonction membre de notre signature souhaitée et vérifier si & t :: opérateur () code> est de ce type: p> xxx pré>

    Maintenant Vérifiez code> sera une instanciation de modèle valide si C code> a effectivement un Void c :: opérateur () () const code>. Mais pour faire cela, nous devons d'abord combiner C code> et la signature à un pointeur de fonction membre. Comme nous l'avons déjà vu, nous devons nous inquiéter de deux cas supplémentaires que nous n'avions pas à prendre en charge pour des fonctions libres: const code> et volatile code> fonctions. En plus de cela, il est à peu près la même chose: p> xxx pré>

    mettre cela et nos résultats concernant le code> contrôlent code> assister ensemble, nous obtenons notre chèque métacone pour des objets de fonctions : P>

    namespace // implementation detail
    {
        // build R (*)(Args...) from R (Args...)
        // compile error if signature is not a valid function signature
        template <typename, typename>
        struct build_free_function;
    
        template <typename F, typename R, typename ... Args>
        struct build_free_function<F, R (Args...)>
        { using type = R (*)(Args...); };
    
        // build R (C::*)(Args...) from R (Args...)
        //       R (C::*)(Args...) const from R (Args...) const
        //       R (C::*)(Args...) volatile from R (Args...) volatile
        // compile error if signature is not a valid member function signature
        template <typename, typename>
        struct build_class_function;
    
        template <typename C, typename R, typename ... Args>
        struct build_class_function<C, R (Args...)>
        { using type = R (C::*)(Args...); };
    
        template <typename C, typename R, typename ... Args>
        struct build_class_function<C, R (Args...) const>
        { using type = R (C::*)(Args...) const; };
    
        template <typename C, typename R, typename ... Args>
        struct build_class_function<C, R (Args...) volatile>
        { using type = R (C::*)(Args...) volatile; };
    
        // determine whether a class C has an operator() with signature S
        template <typename C, typename S>
        struct is_functor_with_signature
        {
            typedef char (& yes)[1];
            typedef char (& no)[2];
    
            // helper struct to determine that C::operator() does indeed have
            // the desired signature; &C::operator() is only of type 
            // R (C::*)(Args...) if this is true
            template <typename T, T> struct check;
    
            // T is needed to enable SFINAE
            template <typename T> static yes deduce(check<
                typename build_class_function<C, S>::type, &T::operator()> *);
            // fallback if check helper could not be built
            template <typename> static no deduce(...);
    
            static bool constexpr value = sizeof(deduce<C>(0)) == sizeof(yes);
        };
    
        // determine whether a free function pointer F has signature S
        template <typename F, typename S>
        struct is_function_with_signature
        {
            // check whether F and the function pointer of S are of the same
            // type
            static bool constexpr value = std::is_same<
                F, typename build_free_function<F, S>::type
            >::value;
        };
    
        // C is a class, delegate to is_functor_with_signature
        template <typename C, typename S, bool>
        struct is_callable_impl
            : std::integral_constant<
                bool, is_functor_with_signature<C, S>::value
              >
        {};
    
        // F is not a class, delegate to is_function_with_signature
        template <typename F, typename S>
        struct is_callable_impl<F, S, false>
            : std::integral_constant<
                bool, is_function_with_signature<F, S>::value
              >
        {};
    }
    
    // Determine whether type Callable is callable with signature Signature.
    // Compliant with functors, i.e. classes that declare operator(); and free
    // function pointers: R (*)(Args...), but not R (Args...)!
    template <typename Callable, typename Signature>
    struct is_callable
        : is_callable_impl<
            Callable, Signature,
            std::is_class<Callable>::value
          >
    {};
    


0 commentaires

1
votes

Bien que cela ne fonctionne pas pour les fonctions surchargées, pour tous les autres cas (fonctions libres, classes implémentées opérateur () code>, et lambdas) Cette courte solution fonctionne dans C ++ 11:

template <typename T, typename Signature>
struct is_callable: std::is_convertible<T,std::function<Signature>> { };


0 commentaires

1
votes

Vous pouvez utiliser le concept suivant dans C ++ 20

template<typename F>
concept FunctionObject = requires (F) {
    &F::operator();
};


0 commentaires