2012-01-31 66 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中可用。