通过参数传递lambda(无函数类型模板)

时间:2018-08-22 06:46:29

标签: c++ lambda function-pointers c++17

我想在不使用函数模板但使用std::function或键入函数指针的情况下传递lambda。

#include <iostream>
#include <list>
#include <functional>

template<typename T>
std::list<T> mapll(std::function<T(T)> f, std::list<T> l) {
    for(auto it = l.begin(); it != l.end(); ++it) {
        *it = f(*it);
    }
}
int main() {
    // Make it
    std::list<int> l = std::list<int> {1,2,3};
    // Map it
    mapll([](int n) { return n * 2; }, l);
    // Print it
    std::cout << "{";
    for (auto it = l.begin(); it != l.end(); ++it) {
        if (it == l.begin())
            std::cout << "," << *it;
        std::cout << *it;
    }
} 

但是我没有成功致电mapll。错误是:

clang++ -g -DNDEBUG -std=c++17 -Wno-switch-bool -lc main.cpp -o main

main.cpp:51:2: error: no matching function for call to 'mapll'
        mapll([](int n) { return n * 2; }, l);

^~~~~ main.cpp:42:14: note: candidate template ignored: could not match 
'function<type-parameter-0-0 (type-parameter-0-0)>' against
    '(lambda at main.cpp:51:8)' std::list<T> mapll(std::function<T(T)> f, std::list<T> l) {

2 个答案:

答案 0 :(得分:2)

不推论std::function参数,因为lamdbas是可转换为std::function对象的不同类型,但是此转换不是类型推导的一部分。这是std::function的工作方式:

template<typename T>
void mapll(std::function<T(T)> f, std::list<T>& l) {
    for(auto it = l.begin(); it != l.end(); ++it) {
        *it = f(*it);
    }
}

/* ... */

mapll(std::function<int(int)>{[](int n) { return n * 2; }}, l);

正如您提到的使用函数指针的可能解决方案,就是这样:

template<typename T>
void mapll(int (*f)(int),  std::list<T>& l) {
    /* Function body identical to the one above. */
}

/* ... */

/* The stateless lambda converts to the function pointer: */
mapll([](int n) { return n * 2; }, l);

不幸的是,以上函数指针在int上运行,在std::list<T>的函数模板中没有意义。使用通用函数指针,mapll函数签名将更改为

template<typename T>
void mapll(T (*f)(T),  std::list<T>& l)

但是同样,没有推论int参数/返回类型的lamdba与该函数指针匹配。您必须显式转换它:

mapll(static_cast<int(*)(int)>([](int n) { return n * 2; }), l);

请注意,正如注释中指出的那样,您还应该考虑std::transform。而且我更改了mapll函数模板的返回值(无效)和第二个参数类型(至左值引用)。

答案 1 :(得分:1)

错误原因在这里已得到很好的解释:C++11 does not deduce type when std::function or lambda functions are involved

如果需要通用解决方案,则需要通过函数指针类型定义lambda类型:

typedef int(*funPtrType)(int); // define function pointer type

template<typename T>
void mapll(const funPtrType f, std::list<T>& l) {
   // code here
}

然后将其命名为:

mapll([](int n) { return n * 2; }, l);

以上解决方案只能用于int类型。如果要使用通用数据类型,则需要使用以下代码:

template<typename T>
void mapll(T (*f)(T),  std::list<T>& l) {
   //  code here
}

然后您可以:

  1. 明确将lambda转换为函数指针类型:

    mapll(static_cast<int(*)(int)>([](int n) { return n * 2; }), l);
          ^^^^^^^^^^^^^^^^^^^^^^^^
    

  2. 明确指定或限定模板参数T的类型 喜欢:

    mapll<int>([](Type n) { return n * 2; }, l);
         ^^^^^
    

但是,最简单的解决方案是为函数指针添加一个更多的模板参数,并让编译器确定类型。

template<typename T, typename FunPtr> // template para for function pointer
void mapll(FunPtr f, std::list<T>& l) {
    // code here
}

注意:您不会从该函数返回列表。因此,使其成为void函数,并如图所示通过ref传递列表。