2012-01-31 31 views
12

如果一个参数是一个C++函数对象(函数),我该如何推导静态?is_functor C++特征类可能吗?

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

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

+0

'is_function :: value'怎么样? – Fiktik 2012-01-31 17:03:40

+1

http://groups.google.com/group/comp.lang.c++.moderated/msg/e5fbc9305539f699可能会对您感兴趣。 – pmr 2012-01-31 17:14:35

+1

你只是想测试函数或任何可调用的对象吗?似乎某些SFINAE使用'result_of'特征可以识别任何可调用类型。我有点惊讶,似乎没有任何'std :: is_callable'特质。 – bames53 2012-01-31 19:40:28

回答

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() 
{ 
} 
+0

'typename decltype'?如果'operator()'超载,你的解决方案将不起作用。 – kennytm 2012-02-01 15:40:38

+0

您对函数的定义不完整。一个标准的函子是一个函数指针或一个重载'operator()'的对象。 – 2012-02-01 15:59:31

+0

我发布了一个不同的解决方案; @MaximYegorushkin,但新的不会改变,嗯 – David 2012-02-01 16:00:14

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

现在所有剩下要做的就是比较,我们正与免费的功能部分进行:

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

+0

哇。哇。哇。哇。 – mark 2016-11-20 00:55:40

0

虽然这并不重载功能,所有其他情况下(免费函数,类实施工作实例operator()和lambdas)这个简短的解决方案在C++ 11中工作:

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

注意:std::is_callable将在C++ 17中可用。