2012-04-03 90 views
41

不推斷類型當我定義該函數,C++ 11時的std ::函數或lambda函數涉及

template<class A> 
set<A> test(const set<A>& input) { 
    return input; 
} 

我可以在代碼使用test(mySet)其他地方打電話,而不必顯式地定義模板類型。然而,當我使用下面的函數:

template<class A> 
set<A> filter(const set<A>& input,function<bool(A)> compare) { 
    set<A> ret; 
    for(auto it = input.begin(); it != input.end(); it++) { 
     if(compare(*it)) { 
      ret.insert(*it); 
     } 
    } 
    return ret; 
} 

當我打電話使用此功能filter(mySet,[](int i) { return i%2==0; }); 我收到以下錯誤:

error: no matching function for call to ‘filter(std::set&, main()::)’

然而,所有這些版本的工作:

std::function<bool(int)> func = [](int i) { return i%2 ==0; }; 
set<int> myNewSet = filter(mySet,func); 

set<int> myNewSet = filter<int>(mySet,[](int i) { return i%2==0; }); 

set<int> myNewSet = filter(mySet,function<bool(int)>([](int i){return i%2==0;})); 

爲什麼當我將lambda函數directl時,C++ 11無法猜測模板類型y表達式內部沒有直接創建std::function

編輯:

每呂克丹東的意見在評論,這裏是功能我早些時候曾不需要模板被明確傳遞了一個替代方案。

template<class A,class CompareFunction> 
set<A> filter(const set<A>& input,CompareFunction compare) { 
    set<A> ret; 
    for(auto it = input.begin(); it != input.end(); it++) { 
     if(compare(*it)) { 
      ret.insert(*it); 
     } 
    } 
    return ret; 
} 

無需模板即可調用set<int> result = filter(myIntSet,[](int i) { i % 2 == 0; });

編譯器甚至可以在某種程度上使用新的decltype關鍵字並使用新函數返回類型語法來猜測返回類型。下面是一組轉換爲地圖,使用一個濾波函數,並且基於該值生成密鑰一個函數的一個例子:

template<class Value,class CompareType,class IndexType> 
auto filter(const set<Value>& input,CompareType compare,IndexType index) -> map<decltype(index(*(input.begin()))),Value> { 
    map<decltype(index(*(input.begin()))),Value> ret; 
    for(auto it = input.begin(); it != input.end(); it++) { 
     if(compare(*it)) { 
      ret[index(*it)] = *it; 
     } 
    } 
    return ret; 
} 

它也可以不直接使用模板調用,因爲

map<string,int> s = filter(myIntSet,[](int i) { return i%2==0; },[](int i) { return toString(i); }); 
+5

與你的問題無關,但你確實意識到你的'filter'本質上等同於'std :: copy_if'的非通用版本,不是嗎? – 2012-04-03 17:45:07

+0

啊,我不知道std :: copy_if,謝謝你指出。然而,這是一組更大的4個函數的一部分,其中一個在過濾時轉換set => map,而我沒有看到用copy_if實現這個函數的方法,並允許用戶使用集合中的值生成密鑰。爲了使用的一致性,我選擇這樣做。 – Datalore 2012-04-03 18:27:31

回答

50

問題在於lambda的性質。它們是具有根據標準的一組固定屬性的函數對象,但它們是而不是的函數。該標準確定lambdas可以轉換爲std::function<>與確切類型的參數,如果他們沒有狀態,函數指針。

但這並不意味着拉姆達是一個std::function也不是一個函數指針。它們是實施operator()的獨特類型。

另一方面,類型演繹只會推導出確切的類型,沒有轉換(除const/volatile限定外)。由於lambda不是std::function,因此編譯器無法推斷出調用中的類型:filter(mySet,[](int i) { return i%2==0; });可能是任何std::function<>實例。

至於其他例子,在第一個例子中,您將lambda轉換爲函數類型,然後傳遞它。編譯器可以推斷出那裏的類型,如第三個示例中std::function是相同類型的右值(臨時)。

如果您向模板提供實例化類型int,則第二個工作示例中的演繹不起作用,編譯器將使用該類型,然後將lambda轉換爲適當的類型。

+11

請注意,它不是執行轉換爲'std :: function'的lambda,而是接受任何可調用的'std :: function'。 – Xeo 2012-04-25 04:38:21

+0

類型扣除也將基地類型「轉化」完成。 – Yakk 2015-03-23 14:13:22

+3

可以通過禁用「比較」參數的推導來使其工作。我們可以將這個簽名做到這一點:'模板 設置濾波器(常量設置&輸入 ,類型名稱NOOP <函數> ::類型比較 )'其中'Noop'被定義爲'模板 結構NOOP { typedef T型; };' – 2015-06-29 15:45:54

7

忘掉你的情況。因爲分析過於複雜。

就拿這個簡單的例子:

template<typename T> 
struct X 
{ 
    X(T data) {} 
}; 

template<typename T> 
void f(X<T> x) {} 

現在叫f爲:

f(10); 

在這裏,你也許會認爲T會被推斷爲int,因此,上述功能電話應該工作。那麼,情況並非如此。爲了讓事情簡單,設想有另一個構造這需要int爲:

template<typename T> 
struct X 
{ 
    X(T data) {} 
    X(int data) {} //another constructor 
}; 

現在什麼T應該推斷,當我寫f(10)?那麼,T可能任何類型。

請注意,可能有許多其他此類情況。採取這種專業化,比如:

template<typename T> 
struct X<T*>   //specialized for pointers 
{ 
    X(int data) {}; 
}; 

現在什麼T應推斷爲呼叫f(10)?現在看起來更難了。

因此它是不可推論的上下文,這就解釋了爲什麼你的代碼不適用於std::function這是一個相同的情況—只是看起來很複雜的表面。需要注意的是lambda表達式的類型std::function —的不是他們基本上編譯器生成的類的實例(即他們不同類型比std::function的仿函數)。