使用带有 lambda 函数指针的模板

Posted

技术标签:

【中文标题】使用带有 lambda 函数指针的模板【英文标题】:Using a template with lambda function pointer 【发布时间】:2020-05-30 11:46:00 【问题描述】:

我在玩一些 C++11 功能,遇到了以下问题:

#include <iostream>
#include <vector>

template <class T>
void map(std::vector<T>& values, T(*func)(T))  
    for (int &value : values) 
        value = func(value);
    


int mul2(int x) 
    return 2*x;


auto mul3 = [](int value) 
    return value * 3;
;

int main() 
    std::vector<int> v =  1,2,3,4,5 ;
    map(v, mul3);
    for (auto value : v) 
        std::cout << value << std::endl;
    

使用mapmul2 可以按预期工作,但是当我使用mul3 函数时,它会出现编译错误。我希望 auto 在这种情况下会给我一个 int 函数指针,但这里似乎不是这种情况。任何人都可以解释这种行为吗?

【问题讨论】:

Lambda 不是函数指针。 @VittorioRomeo 我不明白为什么它不起作用,因为在另一个问题中,OP 问题是捕获,同时在我的 sn-p 中没有捕获 @VittorioRomeo 遗憾的是问题已结束,还有其他选择。您还可以使用“函子”模板模式模板化“函数”。见这里:godbolt.org/z/fdHvAP 我重新打开了这个问题,因为链接的副本是关于捕获 lambdas 而这个问题不是。 “重复”问题的答案没有回答这个问题。 尝试为map 函数明确指定类型Tmap&lt;int&gt;( v, mul3 ); 【参考方案1】:

lambda 可以隐式转换为函数指针,但这不是这里失败的原因。相反,编译器无法推断出T,因为在推断期间不会发生 lambda 到函数指针的转换。

main.cpp:5:6: note:   template argument deduction/substitution failed:
main.cpp:21:16: note:   mismatched types 'T (*)(T)' and '<lambda(int)>'
   21 |     map(v, mul3);
      |                ^

编译器可以在T(*)(T)int(*)(int)之间建立联系,并且可以在int(*)(int)和lambda类型之间建立联系,但是它不能在T(*)(T)和lambda类型之间建立联系。

您可以通过为其建立两个连接之一来解决此问题:显式指定函数的模板参数,或将 lambda 转换为函数指针类型。第一个跳过推导步骤,然后隐式 lambda 到函数指针的转换成功。第二个允许推演成功,因为第二个参数是兼容的函数指针类型。

// Option 1: Specifying T allows implicit conversion of the lambda to fnptr
map<int>(v, mul3);

// Option 2a: Cast of lambda to fnptr allows T to be deduced
map(v, static_cast<int(*)(int)>(mul3));

// Option 2b: Converting lambda to fnptr at mul3 initialization allows T to be deduced
int (*mul3)(int) = [](int value) 
    return value * 3;
;

但是,我建议以不同的方式解决此问题——函数没有理由必须与向量、函数指针和整数一起使用。为什么它不能与链表、仿函数和双精度对象一起使用?真的没有理由限制这样的类型;就让它们不管它们是什么,看看实例化是否成功:

template <class TContainer, TFunction>
void map(TContainer & values, TFunction const & func)  
    for (auto &value : values) 
        value = func(value);
    

【讨论】:

这是我评论的答案的一个很好的变体 - 但你是如何设法在这里发布答案的? - 我不能/仍然不能这样做(即使它重新打开了?)... @code_fodder 我什至没有看到那个代码,如果我偷了这个问题,我深表歉意。那不是我的意图。我不知道您为什么无法发布答案...我假设您尝试过刷新页面? 啊,别担心,伙计 - 我认为它被锁定,然后解锁,然后锁定,然后再次解锁! - 因为它现在似乎又被解锁了:) ...我会发布我的尝试,你的可能更好更彻底【参考方案2】:

根据我的评论扩展(当问题被关闭时),您可以使用仿函数模板“模式”模板化函数详细信息:

template <class T, typename Functor>
void map(std::vector<T>& values, Functor func)  
    for (int &value : values) 
        value = func(value);
    

完整示例请参见此处:https://godbolt.org/z/fdHvAP

【讨论】:

以上是关于使用带有 lambda 函数指针的模板的主要内容,如果未能解决你的问题,请参考以下文章

将 lambda 作为模板参数传递给函数指针函数模板化

传递给模板函数时,lambda自动衰减到函数指针

使用带有模板参数的成员函数指针

模板化函数或带有指向基类的指针的函数

我可以将 C++17 无捕获 lambda constexpr 转换运算符的结果用作函数指针模板非类型参数吗?

带有模板的 C++ 函数指针