作为一个练习(请注意我知道std :: transform),我正在尝试实现通常在函数式语言(如C ++中的Haskell)中找到的map函数。这是我的代码:
#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);
显然,g ++无法匹配std::function
类型的常规函数指针,这样就可以为C ++中的所有函数进行一致的类型化。请注意,使用lambda更改square也会导致编译失败(但匹配的类型与nt (&)(int)
到lambda(int)
的更改匹配。
话虽如此,将functional_map
更改为functional_map<int, int>
已成功编译并执行正确的结果。
为什么会这样?我如何修复我的实现,以便编译器推断模板类型?
编辑为了避免与std::map
混淆,正如评论中所建议的那样,我在所有问题中都将“地图”更改为“functional_map”。
答案 0 :(得分: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;
}
std::result_of<F(Args...)>
是一种类型特征,它利用std::declval<T>()
函数和decltype()
说明符的组合来推导类型为{{1}的函数对象的结果类型使用类型F
的参数调用。也就是说,
Args...
相当于:
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]))
类型。
通过std::decay<T>
类型保证它可以存储在向量中(如果函数对象返回引用等)。
或者,您可以将其声明为:
B
或多或少相同。
答案 1 :(得分:1)
模板参数推导不考虑用户定义的转换。
自己进行转换:
vector<int> v2 = functional_map(std::function<int(int)>(square), v);