开发者

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

开发者 https://www.devze.com 2022-12-02 12:41 出处:网络 作者: 斗酒神僧
目录参考下面的代码下面是类型转换运算符实现的一行关键汇编代码没有捕获任何变量的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, i编程nt 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;
  };
  apythonuto sub = [](int x, int y)
  {
    return x - y;
  };
  int z1 = integer_operation_t<add>(2, 3);
  int z2 = do_integer_operation(integer_ope开发者_Python培训ration_t<sub>, 2, 3);
  cowww.devze.comut << "z1 : " << z1 << ", z2 : " << z2 << endl;
}
python

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

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号