在 C++11 中,有没有一种方法可以在调用将任何可调用对象作为参数(包括绑定方法)作为参数的函数时不需要模板参数?

In C++11 is there a way to not require template arguments when calling a function that takes any callable as an argument, including bound methods?

为了拓宽我对 C++11 的理解,我正在尝试编写函数式帮助程序,看看我是否可以让它们的调用不那么冗长。考虑以下代码:

#include <list>

int timesTwo(int x) { return x*2; }
int timesX(int x, int y) { return x*y; }

class Foo {
public:
    Foo(int a) : value(a) {};
    int fooTimesTwo() const { return value*2; };
    int value;
};

template <class T, class U>
std::list<U> myMap(const std::list<T> &list, const std::function<U(const T &)> &func)
{
    std::list<U> result;

    for(typename std::list<T>::const_iterator it = list.begin(); it != list.end(); ++it) {
        result.push_back(func(*it));
    }

    return result;
}

int main()
{
    std::list<int> numbers = {1,2,3,4,5};
    std::list<int> numbers2 = myMap<int,int>(numbers, [] (int x) { return x*2; });
    std::list<int> numbers3 = myMap<int,int>(numbers, &timesTwo);
    std::list<int> numbers4 = myMap<int,int>(numbers, std::bind(timesX, 2, std::placeholders::_1));

    std::list<Foo> fooList = {Foo(1), Foo(2), Foo(3), Foo(4)};
    std::list<int> numbers5 = myMap<Foo,int>(fooList, &Foo::fooTimesTwo);    
    return 0;
}

有没有重写 myMap 以便

  1. 我的代码示例中对它的所有四个调用都不需要任何模板参数,并且...

  2. 只有一个通用实现,我不必为我想调用它的每种类型组合手动编写重载版本?

我尝试将 myMap 的第二个参数更改为第三个模板类型而不是 std::function,但它失败了,因为 a) 第二个模板类型 U 不能'无法推断,并且 b) 即使可以,第四次调用 myMap 也会导致第 20 行出现错误,因为 &Foo::fooTimesTwo 不是函数或函数指针。

我愿意考虑 C++11 的所有不同特性来做到这一点,我并不特别关心它是否会使 myMap 的声明或定义变得迟钝或不可读。我只是想知道这是否可能,如果可能,什么样的技术和 C++11 功能可以实现它。

您可以尝试在 C++11 中实现 std::invoke,但我认为这会非常麻烦。

为广义可调用函数制作函数模板相当简单:

template <class T, class F>
auto myMap(const std::list<T> &list, F&& func)
    -> std::list<typename std::decay<decltype(func(*list.begin()))>::type>
{
    using U = typename std::decay<decltype(func(*list.begin()))>::type;
    std::list<U> result;

    for(typename std::list<T>::const_iterator it = list.begin(); it != list.end(); ++it) {
        result.push_back(func(*it));
    }

    return result;
}

您可以免费获得 expression sfinae。现在只需要处理成员函数指针:

template <class T, class F>
auto myMap(const std::list<T> &list, F&& func)
    -> std::list<typename std::decay<decltype(((*list.begin()).*func)())>::type>
{
    return myMap(list, [=](T const& t){ return (t.*func)(); });
}

现在您可以按预期调用函数了。 demo


同时,您可以将这个丑陋的 for 循环替换为范围为:

for(auto const& elem : list) {
    results.push_back(func(elem));
}

或者使用算法:

std::transform(list.begin(), list.end(), std::back_inserter(result), func);