2015-08-08 67 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' ... [编辑]哦,这就是其他答案已经提出的。 –