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

use*_*370 12 c++ traits functor type-traits c++11

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

template <typename F>
void test(F f) {}
Run Code Online (Sandbox Code Playgroud)

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

nij*_*sen 12

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

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

第一步:自由功能

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

// 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...); };
Run Code Online (Sandbox Code Playgroud)

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

// 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;
};
Run Code Online (Sandbox Code Playgroud)

第二步:类函子

这个涉及的更多一点.我们可以很容易地通过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);
};
Run Code Online (Sandbox Code Playgroud)

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

template <typename T, T> struct check;
Run Code Online (Sandbox Code Playgroud)

现在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; };
Run Code Online (Sandbox Code Playgroud)

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

// 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);
};
Run Code Online (Sandbox Code Playgroud)

第三步:把碎片放在一起

我们差不多完成了.现在我们只需要决定何时使用我们的自由函数,以及何时类函数元函数.幸运的是,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
      >
{};
Run Code Online (Sandbox Code Playgroud)

所以我们最终可以添加最后一块拼图,作为我们的实际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
      >
{};
Run Code Online (Sandbox Code Playgroud)

现在我们清理代码,将实现细节放到匿名命名空间中,这样它们就不能在我们的文件之外访问,并且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
      >
{};
Run Code Online (Sandbox Code Playgroud)

一些测试的Ideone示例

http://ideone.com/7PWdiv


Dav*_*vid 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);     
};
Run Code Online (Sandbox Code Playgroud)

从此答案改变。

它可以像...

template<typename T>
typename std::enable_if<is_functor<T, void(T::*)()>::value>::type func()
{
}
Run Code Online (Sandbox Code Playgroud)