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



我想在这里不使用函数模板,而是使用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) {

不推导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);

不幸的是,上述函数指针在ints 上运行,这在函数模板中对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函数模板的返回值(无效(和第二个参数类型(到左值引用(。

错误的原因在这里得到了很好的解释:当涉及 std::function 或 lambda 函数时,C++11 不会推断类型

如果需要通用解决方案,则需要按函数指针类型定义 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 传递列表,如图所示。

最新更新