2015-08-08 64 views
1

作爲一個練習(注意,我知道std :: transform),我試圖實現通常在函數式語言上找到的映射函數,例如C++中的Haskell 。這是我的代碼:g ++無法推斷功能圖實現的模板類型

#include <iostream> 
#include <vector> 
#include <functional> 

template<typename A, typename B> 
std::vector<B> functional_map (std::function<B(A)> func, std::vector<A> v) 
{ 
    std::vector<B> result(v.size()); 
    for(int i = 0; i < v.size(); ++i) { 
     result[i] = func(v[i]); 
    } 
    return result; 
} 

儘管如此,當這樣調用編譯器無法正確推斷functional_map模板類型:

int square (int x) {return x*x;} 

int main() 
{ 
    std::vector<int> v = {1,2,3,4,5,6,7,8,9,10}; 
    std::vector<int> v2 = functional_map(square, v); 
    for (int y : v2) { 
     std::cout << y << " "; 
    } 
} 

這是G ++所產生的錯誤:

functional_cpp.cpp: In function ‘int main()’: 
functional_cpp.cpp:18:35: error: no matching function for call to ‘functional_map(int (&)(int), std::vector<int>&)’ 
    vector<int> v2 = functional_map(square, v); 
           ^
functional_cpp.cpp:6:44: note: candidate: template<class A, class B> std::vector<B> functional_map(std::function<B(A)>, std::vector<A>) 
template<typename A, typename B> vector<B> functional_map (function<B(A)> func, vector<A> v) { 
              ^
functional_cpp.cpp:6:44: note: template argument deduction/substitution failed: 
functional_cpp.cpp:18:35: note: mismatched types ‘std::function<B(A)>’ and ‘int (*)(int)’ 
    vector<int> v2 = functional_map(square, v); 

顯然,克++失敗以匹配正則函數指針std::function類型,它是由使所有功能一致的打字在C++中。請注意,由拉姆達改變方也沒有編譯(但類型正在對變化相匹配,從nt (&)(int)lambda(int)

話雖這麼說,改變functional_mapfunctional_map<int, int>編譯成功,並用正確的結果執行。

這是爲什麼發生了什麼?我怎樣才能解決我的實現,因此編譯器會推斷出模板類型?

編輯爲了避免混淆std::map,作爲意見建議我在更名「地圖」與「functional_map」所有問題

+0

'使用namespace std'並聲明像'map'和'list'這樣的名字似乎是災難的祕訣。已經停止濫用命名空間,並且無論誰告訴你幾次在頭部周圍說'使用命名空間標準'。 –

+1

@KerrekSB我很清楚這一點,感謝您的反饋。正如我在文章中提到的那樣,這是一個快速練習,所以我並不打擾stl友好,我只是想避免輸入std ::一堆,如果必須的話,把它當作骯髒的黑客。無論如何,也許我應該改變我的問題,不要混淆未來的讀者? – Setzer22

+3

我並沒有因爲對* meducating *用戶的困惑而迷惑讀者。想想那些在你的帖子中發生的可憐的無辜者,並且他們的可塑性大腦中植入了更多的潛意識信息,這是可以接受的代碼... –

回答

2

一種選擇是採取任何可調用的函數對象,並推斷其結果類型:

#include <type_traits> 

template <typename F, typename A, 
      typename B = typename std::decay<typename std::result_of<F&(typename std::vector<A>::const_reference)>::type>::type> 
std::vector<B> functional_map(F func, const std::vector<A>& v) 
{ 
    std::vector<B> result(v.size()); 
    for (int i = 0; i < (int)v.size(); ++i) 
    { 
     result[i] = func(v[i]); 
    } 
    return result; 
} 

DEMO 1

std::result_of<F(Args...)>是一種性狀是利用std::declval<T>()函數和decltype()說明符的組合來推導出結果類型使用Args...類型的參數調用類型爲F的函數對象。也就是說,

typename std::result_of<F&(typename std::vector<A>::const_reference)>::type 

等同於:

decltype(std::declval<F&>()(std::declval<typename std::vector<A>::const_reference>())) 

這是一樣的:

decltype(func(v[i])) 

您不支付類型擦除,但你仍然能夠獲取類型B

通過std::decay<T>您確定的類型可以存儲在向量中(以防函數對象返回引用等)。

或者,你可以聲明爲:

template <typename F, typename A> 
auto functional_map(F func, const std::vector<A>& v) 
    -> std::vector<typename std::decay<decltype(func(v[0]))>::type> 
{ 
    using B = typename std::decay<decltype(func(v[0]))>::type; 
    std::vector<B> result(v.size()); 
    for (int i = 0; i < (int)v.size(); ++i) 
    { 
     result[i] = func(v[i]); 
    } 
    return result; 
} 

DEMO 2

這或多或少是相同的。

+0

謝謝,你的代碼確實有效。但我對你如何取得成果感到困惑。你能否詳細解釋一下? – Setzer22

+0

第二個版本並不完全「相同」,因爲原始版本允許您指定目標類型,這可能很方便。說到方便,一個更通用的解決方案可能是使用模板轉換運算符返回一個魔術代理對象,以便可以從中構建*任何*容器。 –

+0

您可以輕鬆修改該解決方案以允許任何容器作爲參數。 (或者至少是很多容器)。 http://coliru.stacked-crooked.com/a/7c10dd69bd688dfc – rici

1

模板參數推導不考慮用戶定義的轉換。

讓自己轉換:

vector<int> v2 = functional_map(std::function<int(int)>(square), v); 
+0

所以基本上沒有辦法我的代碼將工作,而不是「鑄造」到std :: function? – Setzer22

+1

@ Setzer22:沒有。那麼'functional_map (square,v)'可能會這樣做。你遇到的根本問題是,你試圖從一種語言中提取一個概念,並用完全不同的語言逐字地重新創建它,並且不準確。 C++標準庫算法傾向於在迭代器範圍內工作,而不是實際的容器;如果你遵循這種模式,你會發現這很容易。 –

+0

@ Setzer22:你可以定義一個不同類型的模板,也許使用'std :: result_of' ... [編輯]哦,這就是其他答案已經提出的。 –