是否可以使用is_functor C ++特征类?

时间:2012-01-31 16:56:58

标签: c++ c++11 functor traits typetraits

如果参数是C ++函数对象(仿函数),我如何静态推导出?

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

我尝试了is_function<F>::value,但这不起作用。它似乎也没有is_functor特征,所以也许这是不可能的。我似乎只是在寻找一个特定的成员函数,在本例中是函数调用运算符:F::operator()

4 个答案:

答案 0 :(得分:12)

可以创建这样一个特征,有两个限制:

  1. 对于编译器,自由函数与重载operator()的类函子有根本的不同。因此,我们必须在实施时单独处理这两种情况。这不是用法问题,我们可以隐藏用户的这个实现细节。
  2. 我们需要知道您要调用的函数的签名。这通常不是问题,它确实具有很好的副作用,我们的特性能够很好地处理过载。
  3. 第一步:免费功能

    让我们从免费功能开始,因为它们更容易被发现。当给定函数指针时,我们的任务是确定该函数指针的签名是否与作为第二个模板参数传递的签名匹配。为了能够比较这些,我们要么需要掌握底层函数签名,要么创建我们签名的函数指针。我随意选择了后者:

    // 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...); };
    

    现在剩下要做的就是比较,我们完成了自由功能部分:

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

    第二步:课程仿函数

    这个更多涉及。我们可以很容易地通过SFINAE检测一个类是否定义了operator()

    template <typename T>
    struct defines_functor_operator
    {
        typedef char (& yes)[1];
        typedef char (& no)[2];
    
        // we need a template here to enable SFINAE
        template <typename U> 
        static yes deduce(char (*)[sizeof(&U::operator())]);
        // fallback
        template <typename> static no deduce(...);
    
        static bool constexpr value = sizeof(deduce<T>(0)) == sizeof(yes);
    };
    

    但这并不能告诉我们是否存在我们所需的功能签名!幸运的是,我们可以在这里使用一个技巧:指针是有效的模板参数。因此,我们可以首先使用我们所需签名的成员函数指针,并检查&T::operator()是否属于该类型:

    template <typename T, T> struct check;
    

    如果check<void (C::*)() const, &C::operator()>确实有C,那么void C::operator()() const只会是有效的模板实例化。但要做到这一点,我们首先必须将C和签名组合成一个成员函数指针。正如我们已经看到的,我们需要担心两个额外的案例,我们不需要关心自由函数:constvolatile函数。除此之外它几乎相同:

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

    将这个和我们关于check辅助结构的发现放在一起,我们得到了函数对象的检查元函数:

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

    第三步:将各个部分放在一起

    我们差不多完成了。现在我们只需要决定何时使用我们的自由函数,以及何时类函数元函数。幸运的是,C ++ 11为我们提供了一个std::is_class特征,我们可以用它来实现。所以我们要做的就是专注于布尔参数:

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

    所以我们最终可以添加最后一块拼图,即我们实际的is_callable特征:

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

    现在我们清理代码,将实现细节放到匿名命名空间中,这样它们就不能在我们的文件之外访问,并且在我们的项目中有一个很好的is_callable.hpp

    完整代码

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

    包含一些测试的Ideone示例

    http://ideone.com/7PWdiv

答案 1 :(得分:1)

你可以在c++20中使用以下概念

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

答案 2 :(得分:0)

template<typename T, typename Sign>                                 
struct is_functor 
{                                                                   
    typedef char yes[1];                                            
    typedef char no [2];                                            
    template <typename U, U> struct type_check;                     
    template <typename _1> static yes &chk(type_check<Sign, &_1::operator()>*);
    template <typename   > static no  &chk(...);                    
    static bool const value = sizeof(chk<T>(nullptr)) == sizeof(yes);     
};

this answer改变。

它可以像...一样使用。

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

答案 3 :(得分:0)

虽然这不适用于重载函数,但对于所有其他情况(自由函数,实现trait和lambdas的类),这个简短的解决方案适用于C ++ 11:

operator()

注意:std::is_callable将在C ++ 17中提供。

相关问题