2012-02-11 61 views
17

函數名爲test以std :: function <>作爲其參數。C++ 11 variadic std ::函數參數

template<typename R, typename ...A> 
void test(std::function<R(A...)> f) 
{ 
    // ... 
} 

但是,如果我做到以下幾點:

void foo(int n) { /* ... */ } 

// ... 

test(foo); 

編譯器(GCC 4.6.1)說no matching function for call to test(void (&)(int))

要使最後一行test(foo)編譯並正常工作,我該如何修改test()函數?在test()函數中,我需要f,其類型爲std :: function <>。

我的意思是說,是否有任何模板技巧讓編譯器確定函數的簽名(例如foo),並自動將其轉換爲std::function<void(int)>

編輯

我想使這個工作lambda表達式(兩人均表示,無國籍)爲好。

回答

11

它看起來像你想使用重載

template<typename R, typename ...A> 
void test(R f(A...)) 
{ 
    test(std::function<R(A...)>(f)); 
} 

這種簡單的實現,如果不是全部,你會嘗試通過功能將接受最。異域功能將被拒絕(如void(int...))。更多的工作會給你更通用的。

+0

什麼lambda表達式(兩人均表示,無國籍)? – 2012-02-11 17:25:27

+4

@丹尼爾,你運氣不好。或者讓'test'接受任何東西('T')。無論如何,std :: function'不會完全拒絕不兼容的函數對象,所以限制函數模板參數類型的目標似乎對我來說並不是太有用。 – 2012-02-11 17:27:46

+1

我用'(T)'試過了,但是,怎麼能'std :: function '?看來我可以使用'std :: result_of <>'來獲得'R',但是'A ...'? – 2012-02-11 17:33:22

4

除非您處於'二元定界'(例如動態庫,'不透明'API),否則通常不建議接受std::function,因爲您剛剛目睹它們會對超載造成嚴重破壞。當函數實際上按值取std::function時,構造對象以避免重載問題(如果函數完全被重載)通常是調用者的負擔。

由於您已經編寫了模板,因此您可能並未使用std::function(作爲參數類型)來獲得類型刪除的好處。如果你想要做的是檢查任意仿函數,那麼你需要一些特徵。例如。 Boost.FunctionTypes具有諸如result_typeparameter_types的特徵。一個最小的,功能例如:

#include <functional> 

#include <boost/function_types/result_type.hpp> 
#include <boost/function_types/parameter_types.hpp> 
#include <boost/function_types/function_type.hpp> 

template<typename Functor> 
void test(Functor functor) // accept arbitrary functor! 
{ 
    namespace ft = boost::function_types; 

    typedef typename ft::result_type<Functor>::type result_type; 
    typedef ft::parameter_types<Functor> parameter_types; 
    typedef typename boost::mpl::push_front< 
     parameter_types 
     , result_type 
    >::type sequence_type; 
    // sequence_type is now a Boost.MPL sequence in the style of 
    // mpl::vector<int, double, long> if the signature of the 
    // analyzed functor were int(double, long) 

    // We now build a function type out of the MPL sequence 
    typedef typename ft::function_type<sequence_type>::type function_type; 

    std::function<function_type> function = std::move(functor); 
} 

最後一點,我不建議反思函子(即督促其結果類型和參數類型)中作爲根本就不多態仿函數工作的一般情況。考慮幾個重載operator():那麼沒有'規範'的結果類型或參數類型。使用C++ 11時,最好'熱切地'接受任何類型的函數,或者根據需要使用像SFINAE或static_assert這樣的技術來限制它們,並且稍後(當參數可用時)使用std::result_of檢查結果類型一組給定的參數。需要限制前方的情況是當目標是將仿函數存儲到例如一個std::function<Sig>的容器。

爲了體會上一段我的意思,它足以用多態函子來測試上面的代碼片段。

5

std::function實現可調用接口,即它看起來像一個功能,但是,這並不意味着你應該需要調用的對象是std::function秒。

template< typename F > // accept any type 
void test(F const &f) { 
    typedef std::result_of< F(args) >::type R; // inspect with traits queries 
} 

鴨子打字是模板元編程中的最佳策略。當接受一個模板參數時,不具體,只需讓客戶端實現接口。

如果你真的需要例如std::function重新定向的變量或一些瘋狂的那樣,你知道的輸入是原始函數指針,可以分解原始函數指針類型,它reconsitute成std::function

template< typename R, typename ... A > 
void test(R (*f)(A ...)) { 
    std::function< R(A ...) > internal(f); 
} 

,因爲現在已經被封裝在函數中,用戶可以不通過std::function。您可以將現有的代碼保留爲另一個重載並委託給它,但要小心保持接口簡單。

至於有狀態lambda,我不知道如何處理這種情況。它們不會分解爲函數指針,並且據我所知,參數類型不能被查詢或推斷。這個信息對於實例化std::function是必要的,好或壞。

+1

我認爲正確的術語是動態綁定 - 使用術語'duck typing'已被棄用 – serup 2017-01-26 11:34:07

+0

@serup Google =>「後期綁定或動態綁定是一種計算機編程機制,或者用參數調用的函數在運行時按名稱查找。「不適用於模板。 – Potatoswatter 2017-01-26 11:52:17

+0

那麼爲什麼要使用術語鴨打字? – serup 2017-01-26 12:04:56

2

這是一箇舊的,我似乎無法找到很多關於同一主題,所以我想我會繼續並在一個說明。

編譯於GCC 4.8.2,以下工作:

template<typename R, typename... A> 
R test(const std::function<R(A...)>& func) 
{ 
    // ... 
} 

但是,你不能僅僅通過傳遞你的指針,lambda表達式等。但是,下面的兩個例子都工作,把它它:

test(std::function<void(int, float, std::string)>(
     [](int i, float f, std::string s) 
     { 
      std::cout << i << " " << f << " " << s << std::endl; 
     })); 

另外:

void test2(int i, float f, std::string s) 
{ 
    std::cout << i << " " << f << " " << s << std::endl; 
} 

// In a function somewhere: 
test(std::function<void(int, float, std::string)>(&test2)); 

的這些缺點應該站出來很明顯:喲你必須爲它們顯式聲明std :: function,這可能看起來有點難看。這就是說,雖然我把它與一個元組一起拋出,它被擴展爲調用傳入函數,並且它可以工作,只需要多一點的明確說明你在做什麼調用測試函數。

示例代碼,包括元組的事情,如果你想用它玩:http://ideone.com/33mqZA

+1

爲了踢球,我想出了一個使用index_sequence(在C++ 14中添加,但易於在C++ 11中實現)的方法和一個function_traits風格的結構來提供一些東西這將採取任何lambda,仿函數或函數,並使用它。 [http://ideone.com/LNpj74](http://ideone.com/LNpj74)顯示了一個工作的例子。但是,請注意,對於使用2+運算符()重載的函子,它需要額外的接口來指定要使用的類型。 – user3694249 2014-06-09 02:21:00

+0

正如Potatoswatter在上面提到的那樣,當模板可用時,使用'std :: function'是一堆動態分配開銷和樣板文件沒有任何好處。更好的辦法是製作一個可以接受lambda的通用函數模板,而不是將'std :: function'全部拉入。 – 2016-08-27 16:36:33

相關問題