2011-09-30 96 views
5

我正在嘗試編寫一個模板函數,它接受取決於模板參數的std::function。不幸的是,編譯器無法正確消除std::function的參數。這裏是一些簡單的例子代碼:C++ 11模板函數,它取決於模板參數的std ::函數

#include <iostream> 
#include <functional> 

using namespace std; 

void DoSomething(unsigned ident, unsigned param) 
{ 
    cout << "DoSomething called, ident = " << ident << ", param = " << param << "\n"; 
} 

template < typename Ident, typename Param > 
void CallFunc(Ident ident, Param param, std::function< void (Ident, Param) > op) 
{ 
    op(ident, param); 
} 

int main() 
{ 
    unsigned id(1); 
    unsigned param(1); 

    // The following fails to compile 
    // CallFunc(id, param, DoSomething); 

    // this is ok 
    std::function< void (unsigned, unsigned) > func(DoSomething); 
    CallFunc(id, param, func); 

    return 0; 
} 

如果我稱之爲有以下模板:

CallFunc(id, param, DoSomething); 

我收到以下錯誤:

function-tpl.cpp:25: error: no matching function for call to CallFunc(unsigned int&, unsigned int&, void (&)(unsigned int, unsigned int))

如果我明確地創建一個std ::正確類型的功能(或投它)問題消失:

std::function< void (unsigned, unsigned) > func(DoSomething); 
CallFunc(id, param, func); 

我將如何編碼,以便不需要顯式臨時?

回答

2

如果你正在使用的模板,你可以避開std::function完全,除非出於某種原因,你想專門限制功能取std::function

template < typename Ident, typename Param, typename Func > 
void CallFunc(Ident ident, Param param, Func op) 
{ 
    op(ident, param); 
} 
+0

您是否忘記在違規行之前刪除註釋字符「//」?我有GCC4.6.1,GCC拒絕上述線路。 –

+0

@litb * D'OH!* 發現得好。 –

0

您可以在線轉換或使用bind。也不是特別漂亮,但他們把工作做好

CallFunc(id, param, std::function<void(unsigned, unsigned)>(DoSomething)); 

CallFunc(id, param, std::bind(DoSomething, std::placeholders::_1, std::placeholders::_2)); 

+0

std :: bind會產生以下錯誤: function-tpl.cpp:37:error:沒有匹配函數調用CallFunc(unsigned int&,unsigned int&,std :: _ Bind ,std :: _佔位符<2>))(unsigned int,unsigned int)>)' – mark

+0

@mark:你說得對,'bind'不允許參數推理,你必須說'CallFunc <無符號,無符號>(...)'。所以...只是使用顯式轉換。 –

8

您需要將第三個函數參數作爲其中模板參數的非推導上下文。然後,編譯器不會將參數類型與參數類型進行比較,也不會考慮所有隱式轉換(標準說,並且C++ 0x進一步闡明瞭這一點,即對於沒有模板參數推導位置的函數參數,所有隱式轉換可以彌合差異)。

template < typename T > struct id { typedef T type; }; 

template < typename Ident, typename Param > 
void CallFunc(Ident ident, Param param, 
       typename id<std::function< void (Ident, Param) >>::type op) 
{ 
    op(ident, param); 
} 

相反的id可以使用boost::identity。在C++ 0x中,並支持它的編譯器,你可以使用別名模板

template < typename T > using nondeduced = typename id<T>::type; 

有一個更可讀的版本那麼你的代碼變得簡單

template < typename Ident, typename Param > 
void CallFunc(Ident ident, Param param, 
       std::function< nondeduced<void (Ident, Param)> > op) 
{ 
    op(ident, param); 
} 

但是GCC還不支持別名模板。

+0

我原以爲這個參數已經在一個非推導的上下文中。這很好理解! –

+0

第一個版本如何包裝整個'函數'類型,但第二個版本只包裝模板參數? –

+0

@KerrekSB由於在C++ 03中編寫語言的編譯器有不同的解釋,我將整個參數類型包裝到一個非推導的上下文中。一些編譯器會在進入非推導的上下文(即'std :: function )>'parts)之前選擇該類型的頂級部分,並將其與參數進行比較,從而觸發推理失敗。其他編譯器不會那樣做。如果環繞整個類型,則不會留下「未觸及」的部分,因此全部都是......(黑洞),並且不會出現不匹配。請參閱http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1184 –