Jeh*_*han 4 c++ templates function-pointers c++11
我一直在尝试在C++ 11中实现Python 的函数映射.它似乎适用于任何类型的可调用对象,但如果我希望它与函数模板一起使用,我必须指定模板类型参数.例:
#include <iostream>
#include <list>
template<typename T>
T abs(T x)
{
return x < 0 ? -x : x;
}
int main()
{
std::list<int> li = { -1, -2, -3, -4, -5 };
for (auto i: map(&abs<int>, li))
{
std::cout << i << std::endl;
}
}
Run Code Online (Sandbox Code Playgroud)
它工作正常,但我希望它int从函数的第二个参数推导出参数,因此能够写:
for (auto i: map(&abs, li))
{
std::cout << i << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
我的map功能写成:
template<typename Callable, typename Container>
auto map(const Callable& function, Container&& iter)
-> MapObject<Callable, Container>
{
return { function, std::forward<Container>(iter) };
}
Run Code Online (Sandbox Code Playgroud)
哪里MapObject是实施的一部分,而不是一个真正的问题.我怎样才能更改其定义,以便Callable可以从Container对象中推导出对象的模板类型?例如,如何能map知道,我们必须使用abs<int>给定一个abs一个的时候list<int>给出?
它工作正常,但我希望它从函数的第二个参数推导出int参数,因此能够写:
for (auto i: map(&abs, li))
{
std::cout << i << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
问题在于它abs不是函数,而是函数模板,因此没有地址 abs,尽管存在&abs<int>,因为abs<int>(特化)确实是一个函数(从模板生成).
现在问题是你真正想要解决的问题,特别是你必须意识到C++是一种静态类型语言,其中python是一种动态类型语言.我不清楚你在不同层面上想要达到的目标.例如,mappython中的函数std::transform在C++中具有等价物:
a = [ 1, 2, 3 ]
a = map(lambda x: 2*x, a)
std::vector<int> v{1,2,3};
std::transform(v.begin(),v.end(),v.begin(),[](int x){ return 2*x; });
Run Code Online (Sandbox Code Playgroud)
我稍微欺骗的地方,因为在python中它会创建一个不同的容器但C++ transform在迭代器级别工作并且知道没有容器,但你可以获得相同的效果:
std::vector<int> v{1,2,3};
std::vector<int> result;
// optionally: result.reserve(v.size());
std::transform(v.begin(),v.end(),
std::back_inserter(result),
[](int x) { return 2*x; });
Run Code Online (Sandbox Code Playgroud)
我建议你学习语言中的习语,而不是试图用其他语言来实现习语......
顺便说一句,如果您愿意让用户指定传递给map函数的仿函数的类型,那么您只需传递模板的名称并让编译器找出您需要的特化:
template <typename Container>
auto map(Container && c,
typename Container::value_type (*f)(typename Container::value_type))
-> MapObject<Callable<T>,Container>;
template <typename T>
T abs(T value);
int main() {
std::vector<int> v{1,2,3,4};
map(v,abs);
}
Run Code Online (Sandbox Code Playgroud)
这不像你想要做的那样通用,因为它只接受函数指针和具体类型(这甚至不那么通用std::transform),它就像编译器看到的那样abs(没有&)它会将它解析为模板,因此对一组专业化.然后它将使用期望的类型来选择一个特化并将其传入.&abs<int>在这种情况下,编译器将隐式地为您做.
另一个更通用的替代方法是不使用函数,而是使用函子.考虑到这一点,您可以定义abs为:
struct abs {
template <typename T>
T operator()(T t) { ...}
};
Run Code Online (Sandbox Code Playgroud)
然后传递函子的副本而不是函数指针.只有在使用对象时,才需要确定将对象传递abs给map函数的过载.来电方面看起来像:
for (auto& element : map(container,abs()))
Run Code Online (Sandbox Code Playgroud)
额外的括号集创建一个类型的对象abs并将其传入.
总的来说,我会尽量避免这种情况.这是一件有趣的事情,你可能会得到一个很好的解决方案,但它很难并需要相当多的c ++专业知识.由于语言不支持它,因此您必须设计一些在语言中有效的东西,并且需要对不同的功能或语法进行折衷.了解选项本身就是一个难题,更难理解妥协,更难以获得良好的解决方案.而良好的解决方案很可能会比同等地道的C++代码更糟.
如果您使用C++编程,请编写C++程序.尝试通过C++编译器编写python代码可能会让您感受到C++的痛苦和python的性能.