2011-10-31 51 views
3

我玩弄lambda函數在GCC 4.6.2,並且想實現這樣的模板「地圖」功能:如何將lambda(C++ 11)傳遞給模板化函數?

template<typename A, typename B> std::vector<B> map(const std::vector<A>& orig, const std::function<B(A)> f) { 
    std::vector<B> rv; 
    rv.resize(orig.size()); 
    std::transform(begin(orig), end(orig), begin(rv), f); 
    return rv; 
} 

這是不行的,因爲測試代碼:

int main(int argc, char **argv) { 
    std::vector<int> list; 
    list.push_back(10); 
    list.push_back(20); 
    list.push_back(50); 

    std::vector<int> transformed = map(list, [](int x) -> int { return x + 1; }); 
    std::for_each(begin(transformed), end(transformed), [](int x) { printf("-> %d\n", x); }); 
    return 0; 
} 

給出了這樣的錯誤:

test.cpp:49:80: error: no matching function for call to ‘map(std::vector<int>&, main(int, char**)::<lambda(int)>)’ 
test.cpp:49:80: note: candidate is: 
test.cpp:6:49: note: template<class A, class B> std::vector<B> map(const std::vector<A>&, std::function<B(A)>) 

如果我刪除模板,並直接使用一個載體,它編譯罰款:

std::vector<int> map(const std::vector<int>& orig, const std::function<int(int)> f) { 
    std::vector<int> rv; 
    rv.resize(orig.size()); 
    std::transform(begin(orig), end(orig), begin(rv), f); 
    return rv; 
} 

所以它必定是我定義模板的方式的一個問題。

有沒有人遇到過這個?我知道lambda是非常新的。

+0

你知道在你的文章的例子中,你在調用'map_'的時候定義了函數'map'?請注意呼叫中的下劃線... :-) –

+0

抱歉,錯別字 - 我從一個文件中粘貼了十幾次不同的嘗試,看我是否可以讓編譯器給我一個更好的線索,告訴它它做了什麼不喜歡。想想我現在已經修好了。 –

回答

0

的問題是,編譯器無法弄清楚爲B.用什麼爲了確定該類型它要使用的功能<>你傳爲f,但你不及格直接使用std :: function <>。你傳入一些你期望用來構造一個函數的東西> <>。爲了做到這種隱含的結構,它需要知道論證的類型。所以你有這個循環依賴關係,其中參數的類型取決於你傳入的內容,但傳入的內容取決於參數的類型。

可以通過指定的模板參數打破這種循環依賴,如map_<int,int>(list, [](int x) -> char { return x + 1; });

(雖然我看到的仿函數實際上返回一個字符,而不是一個int,所以如果類型推演爲你工作在這裏,你會要取回一個vector<char>當你分配結果transformed

然而,由於已經指出的那樣,不能轉換爲vector<int>,一般模板採取仿函數作爲只是一個普通的模板類型:

template<typename A,typename Func> 
auto map_(const std::vector<A>& orig, Func f) -> std::vector<decltype(f(A()))> { 
    std::vector<decltype(f(A()))> rv; 
    /*...*/ 
} 

(我們使用後返回類型,因爲我們需要使用表達式f在返回類型,它是不可用,除非返回類型來算賬。)

這使得模板可以直接推導出仿函數類型並避免任何類型的轉換,並最大限度地允許優化。

也習慣使用迭代器作爲這些函數的參數,在這種情況下,你的函數只是std :: transform的一個包裝,所以你可以直接使用它。我不確定在專門處理矢量的特殊版本中有很多價值。

+0

工作正常! (我固定了「char」的東西;對此抱歉)。所以這聽起來像我缺少2點:1. Lambdas不是類型函數,而是使用隱式構造函數進行轉換。 2.標記lambda類型的唯一真正方法是使用decltype,而lambda表達式實際上沒有其他方式可以描述爲類型。同意這個例子的無意義的特殊性 - 我只是想看看它是否可以完成,而且可以!謝謝你的幫助! –

+0

這個問題的答案有一個使用可變參數模板的類型特徵,這使得更容易詢問lambda的類型。例如,而不是我使用decltype你可以用這個來說'function_traits :: result_type'。 http://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda – bames53

+0

此外我的decltype不工作如果A不是默認可構造的。 'decltype(f(orig [0]))'是另一種選擇。 – bames53

2

你不需要使用std :: function。只需將謂詞參數設置爲模板值即可。例如,

template<typename A, typename B> std::vector<B> map(const std::vector<A>& orig, B f) { 

的std ::功能<>是作爲成員值類型或用於定義非模板化代碼更加有用。

+1

假設您打算爲模板添加一個新的「typename C」,並使參數「C f」具有相同的問題。它不認爲「地圖」功能匹配。 –

0

我正在處理lambda表達式,我注意到你可以在函數定義的參數列表中聲明一個函數指針,當你調用該函數時,如果函數匹配函數,你可以傳遞一個lambda表達式作爲參數原型當然。

#include <iostream> 

#include <vector> 

#include <algorithm> 

#include <iterator> 



template <typename T,typename C> 

struct map { 

    typedef C (*F)(const T&); 

    std::vector<C> rv; 

    map() {} 

    map (const std::vector<T>& o,F f) { 

     rv.resize(o.size()); 

     std::transform (o.begin(),o.end(),rv.begin(),f); 

    } 

    ~map() {} 

    operator std::vector<C>() const { 

     return rv; 

    } 

}; 



int main() { 

    std::vector<int> asd(5,12); 

    std::vector<char> transformed=map<int,char>(asd,[](const int& x)->char {return x+1;}); 

    std::copy (transformed.begin(),transformed.end(),std::ostream_iterator<int>(std::cout," ")); 

} 
相關問題