C++ 如何将Lambda转换成函数指针

目录

没有捕获任何变量的Lambda表达式可以转换成与它的调用原型一致的函数指针。

参考下面的代码:

void example1()
{
    auto add = [](int x, int y)
    {
        return x + y;
    };
    int x = 2, y = 3;
    int z1 = add(x, y);                  // 调用Lambda
    int(*f)(int, int) = add;             // Lambda转换成函数指针
    int z2 = f(x, y);                    // 调用函数
    cout << z1 << ", " << z2 << endl;
}

Lambda是实现了函数调用运算符的匿名类(anonymous class)。对于每一个Lambda,编译器创建匿名类,并定义相应的数据成员存储Lambda捕获的变量。没有捕获变量的Lambda不包含任何含成员变量。

一个没有任何成员变量(包括没有虚函数表指针)的类型,在空指针上调用成员函数也不会有任何的问题,因为它的成员函数不会通过this指针访问内存。

当Lambda向函数指针的转换时,编译器为Lambda的匿名类实现函数指针类型转换运算符。

上面的例子中,编译器实现operator int(*)(int, int)。

下面是Visual C++编译器为语句int(*f)(int, int) = add生成的64位汇编代码:

lea         rcx,[add]  
call        <lambda_0e153cdea67ea404383c23c1022dd325>::operator int (__cdecl*)(int,int) 
mov         qword ptr [f],rax  

第1行,变量"add"的地址存入rcx寄存器;第2行,调用匿名类的函数指针类型转换运算符;第3行,返回结果存入变量f。

提示:在默认的成员函数调用约定__thiscall下,this指针通过rcx寄存器传递, 有关__thiscall的详细内容,请参考:https://docs.microsoft.com/en-us/cpp/cpp/thiscall

下面是类型转换运算符实现的一行关键汇编代码:

lea         rax,[<lambda_0e153cdea67ea404383c23c1022dd325>::<lambda_invoker_cdecl>] 

这一行把匿名类的名为lambda_invoker_cdecl的函数地址存入用于返回结果的寄存器rax。因为只有类的静态函数可以转换成非成员函数指针,所以lambda_invoker_cdecl是静态函数。下面是此函数其中一段汇编代码:

xor         rcx,rcx  
call        <lambda_0e153cdea67ea404383c23c1022dd325>::operator() 

第一行,rcx寄存器清0,即this指针置0;第二行,调用operator()。

综合上面的分析,得出Lambda转换成函数指针的一种可能的实现方式,参考下面的代码:

typedef int(*FUNCADD)(int, int);

// 实现两个整数相加的函数对象
class add_function_object
{
public:
    // 函数调用运算符
    int operator()(int x, int y)const
    {
        return x + y;
    }
    // 函数指针类型转换运算符
    operator FUNCADD()const
    {
        return add;
    }
private:
    static int add(int x, int y)
    {
        add_function_object* obj = nullptr;
        return obj->operator()(x, y);
    }
};

void example2()
{
    int x = 2, y = 3;
    add_function_object add;
    int z1 = add(x, y);
    auto fadd = add.operator FUNCADD();
    int z2 = fadd(x, y);
    cout << z1 << ", " << z2 << endl;
}

从C++17起,没有捕获任何变量的Lambda可以用作值类型模板实参,参考下面的代码:

typedef int (*INTEGER_OPERATION)(int, int);

int do_integer_operation(INTEGER_OPERATION op, int x, int y)
{
    return op(x, y);
}

template <INTEGER_OPERATION op>
int integer_operation_t(int x, int y)
{
    return op(x, y);
}

void example3()
{
    auto add = [](int x, int y)
    {
        return x + y;
    };
    auto sub = [](int x, int y)
    {
        return x - y;
    };
    int z1 = integer_operation_t<add>(2, 3);
    int z2 = do_integer_operation(integer_operation_t<sub>, 2, 3);
    cout << "z1 : " << z1 << ", z2 : " << z2 << endl;
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • ​​C++11系列学习之Lambda表达式

    目录 一.为什么要有lambda表达式? 二.使用语法 捕获列表 mutable影响lambda表达式 std::bind和lambda表达式结合 三.std::function 和lambda表达式选择 前言: 终于在C++11中引入了lambda表达式,lambda最早来源于函数式编程,现代语言慢慢都引入了这个语法,C++也不甘落后,在新标准中加入了lambda表达式. 一.为什么要有lambda表达式? 使用方便,就地声明函数或函数对象,尤其是和bind配合食用更佳 简洁,可以匿名创建,语

  • 一文读懂c++11 Lambda表达式

    1.简介 1.1定义 C++11新增了很多特性,Lambda表达式(Lambda expression)就是其中之一,很多语言都提供了 Lambda 表达式,如 Python,Java ,C#等.本质上, Lambda 表达式是一个可调用的代码单元[1]^{[1]}[1].实际上是一个闭包(closure),类似于一个匿名函数,拥有捕获所在作用域中变量的能力,能够将函数做为对象一样使用,通常用来实现回调函数.代理等功能.Lambda表达式是函数式编程的基础,C++11引入了Lambda则弥补了C

  • C++11 lambda(匿名函数)表达式详细介绍

    目录 前言 概念及基本用法 捕获变量 lambda表达式类型 声明式的编程风格 总结 前言 Lambda(匿名函数)表达式是C++11最重要的特性之一,lambda来源于函数式编程的概念,也是现代编程语言的一个特点. 优点如下: 声明式编程风格:就地匿名定义目标函数或函数对象,有更好的可读性和可维护性. 简洁:不需要额外写一个命名函数或函数对象,,避免了代码膨胀和功能分散. 更加灵活:在需要的时间和地点实现功能闭包. 概念及基本用法 lambda表达式定义了一个匿名函数,并且可以捕获一定范围内的

  • C++ 如何将Lambda转换成函数指针

    目录 没有捕获任何变量的Lambda表达式可以转换成与它的调用原型一致的函数指针. 参考下面的代码: void example1() {     auto add = [](int x, int y)     {         return x + y;     };     int x = 2, y = 3;     int z1 = add(x, y);                  // 调用Lambda     int(*f)(int, int) = add;          

  • C语言中的函数指针学习笔记

    一.定义函数指针 return_type (*func_pointer)(parameter_list) 普通指针变量的定义 int * p; char * pointer; 类型的限定都在变量前面: 函数指针类型的限定是前后都有,前面是返回类型,后面是输入参数. 利用typedef 可以简化上面的表达方式. typedef return_type (*FunctionPointer) (parameter_list); FunctionPointer func_pointer; 这样是不是容易

  • c++函数名指针和函数指针

    目录 前言 例 1 例 2 例 3 前言 我们先来看一下函数指针式如何定义的,假如我们有一个函数int fun(int){…};那么他对应的函数指针写法就应该是int (*p)(int);然后再对他进行赋值,即p=fun;之后你就可以在接下来的地方按p作为函数名来调用它用起来完全和fun一样.(注意这里的p指针并不是只能接受fun这个函数名,任何返回值是int,参数只有一个int的函数都可以把函数名赋给p) 首先说一下C/C++在创建一个变量的时候比如int a;相应的在内存就会分配一个4个字节

  • 实例解析C++中类的成员函数指针

    C语言的指针相当的灵活方便,但也相当容易出错.许多C语言初学者,甚至C语言老鸟都很容易栽倒在C语言的指针下.但不可否认的是,指针在C语言中的位置极其重要,也许可以偏激一点的来说:没有指针的C程序不是真正的C程序. 然而C++的指针却常常给我一种束手束脚的感觉.C++比C语言有更严格的静态类型,更加强调类型安全,强调编译时检查.因此,对于C语言中最容易错用的指针,更是不能放过:C++的指针被分成数据指针,数据成员指针,函数指针,成员函数指针,而且不能随便相互转换.而且这些指针的声明格式都不一样:

  • c++11 符号修饰与函数签名、函数指针、匿名函数、仿函数、std::function与std::bind

    一.符号修饰与函数签名 1.符号修饰 编译器将c++源代码编译成目标文件时,用函数签名的信息对函数名进行改编,形成修饰名.GCC的C++符号修饰方法如下: 1)所有符号都以_z开头 2)名字空间的名字 名字空间(或类)的名字前加上N 名字前还有一个数字,是名字的字符数.比如1C,1是C的长度. 3)函数名 与名字空间一样,函数名前也有数字,比如4func,4是func的字符数. 4)参数 参数以E开头 例子 N::C::func(int) 的函数签名经过修饰为_ZN1N1C4funcEi 2.函

  • C++ 函数指针详细总结

    目录 1.函数指针 1.1 获取函数地址 1.2 声明函数指针 1.3 函数指针传参 1.4 调用函数 2.函数指针进阶 1.函数指针 函数指针顾名思义,就是指向函数的指针. 和数据类似,C++当中函数也有地址,函数的地址是存储函数机器语言代码的内存地址.我们可以将另外一个函数的地址作为参数传入函数,从而实现函数的灵活调用. 1.1 获取函数地址 获取函数地址的方法非常简单,只要使用函数名(后面不跟参数和括号)即可.比如我们有一个函数叫做think,那么think()是调用函数拿到结果,而thi

  • C++函数指针的用法详解

    目录 C++函数指针详细理解 函数指针初识 函数指针作为形参使用 函数指针作为返回值 练习 总结 C++函数指针详细理解 函数指针初识 函数指针是指向函数的指针,并不等于函数,它能够执行函数的操作,可以跟指针变量一起来理解,其基本声明格式为: type (*ptrname)(形参列表); 例子,声明一个比较两个字符串大小的函数指针 bool (*cp)(const string s1, const string s2); 需要注意的是,不要去掉括号!!!如果去掉括号,含义就变成了返回一个bool

  • Python字符串转换成浮点数函数分享

    利用map和reduce编写一个str2float函数,把字符串'123.456'转换成浮点数123.456 from functools import reduce def str2float(s): return reduce(lambda x,y:x+int2dec(y),map(str2int,s.split('.'))) def char2num(s): return {'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':

  • JS自定义函数实现时间戳转换成date的方法示例

    本文实例讲述了JS自定义函数实现时间戳转换成date的方法.分享给大家供大家参考,具体如下: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>获取当前年/月/日(www.jb51.net)</title> </head> <body> <script> function UnixToDate(unixTime

  • php把时间戳转换成多少时间之前函数的实例

    如下所示: function wordTime($time) { $time = (int) substr($time, 0, 10); $int = time() - $time; $str = ''; if ($int <= 2){ $str = sprintf('刚刚', $int); }elseif ($int < 60){ $str = sprintf('%d秒前', $int); }elseif ($int < 3600){ $str = sprintf('%d分钟前', f

随机推荐