4

我一直在尝试在 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;
    }
}

它工作正常,但我希望它int从函数的第二个参数中推断出参数,因此能够编写:

for (auto i: map(&abs, li))
{
    std::cout << i << std::endl;
}

我的map函数写成:

template<typename Callable, typename Container>
auto map(const Callable& function, Container&& iter)
    -> MapObject<Callable, Container>
{
    return { function, std::forward<Container>(iter) };
}

whereMapObject是实施的一部分,在这里不是真正的问题。我怎样才能改变它的定义,以便Callable可以从对象中推断出对象的模板类型Container?例如,当给定 a 时,如何map知道我们必须使用abs<int>给定的?abslist<int>

4

2 回答 2

5

它工作正常,但我希望它从函数的第二个参数中推断出 int 参数,因此能够编写:

for (auto i: map(&abs, li))
{
    std::cout << i << std::endl;
}

问题是它abs不是一个函数,而是一个函数模板,因此没有address-of abs,尽管有&abs<int>, 因为abs<int>(specialization) 确实是一个函数(从模板生成)。

现在问题是您真正想要解决的问题,特别是您必须意识到 C++ 是一种静态类型的语言,而 python 是一种动态类型的语言。我不清楚你想在不同层面上实现什么。例如,python 中的函数在 C++map中具有等效的in:std::transform

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; });

我稍微作弊的地方是因为在 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; });

我建议您学习该语言中的习语,而不是尝试使用其他语言的习语...

顺便说一句,如果您愿意让用户指定传递给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);
}

这不像您尝试做的那样通用,因为它只接受函数指针和具体类型(这std::transform甚至abs&.因此,对于专业化集合。然后它将使用预期的类型来选择一种特化并将其传入。&abs<int>在这种情况下,编译器将为您隐式执行。

另一个更通用的替代方法是不使用函数,而是使用仿函数。考虑到这一点,您可以定义abs为:

struct abs {
   template <typename T>
   T operator()(T t) { ...}
};

然后传递函子的副本而不是函数指针。无需确定在将对象传递给函数时使用的重载absmap仅在使用时确定。调用方看起来像:

for (auto& element : map(container,abs()))

额外的一组括号是创建一个类型的对象abs并将其传入。

总的来说,我会尽量避免这种情况。这是一件有趣的事情,您可能会找到一个好的解决方案,但这会很困难并且需要相当多的 c++ 专业知识。因为该语言不支持它,所以您必须设计一些在该语言中工作的东西,并且需要在不同的特性或语法上做出妥协。了解选项本身就是一个难题,更难理解折衷方案,更难找到好的解决方案。好的解决方案可能会比等效的惯用 C++ 代码更糟糕

如果您使用 C++ 编程,请编写 C++。尝试通过 C++ 编译器编写Python代码可能会给您带来 C++ 的痛苦和 Python 的性能。

于 2013-04-27T20:27:54.083 回答
-1

它不会推断它,因为您从未指定 Callable 是模板。您使 Callable 成为模板模板参数,它应该为您推断其类型。

template<template <typename T> typename Callable, typename Container>
auto map(const Callable<T>& function, Container&& iter)
    -> MapObject<Callable<T>, Container>
{
    return { function, std::forward<Container>(iter) };
}

您可能会被咬,因为您无法获取仍要实例化的模板的地址。不知道为什么你需要地址...

于 2013-04-27T19:24:01.853 回答