什么是C++ lambda函数的默认调用约定?

xml*_*lmx 16 c++ lambda function calling-convention c++11

以下代码是使用VC++ 2012编译的:

void f1(void (__stdcall *)())
{}

void f2(void (__cdecl *)())
{}

void __cdecl h1()
{}

void __stdcall h2()
{}

int main()
{
    f1(h1); // error C2664
    f2(h2); // error C2664

    f1([](){}); // OK
    f2([](){}); // OK

    auto fn = [](){};

    f1(fn); // OK
    f2(fn); // OK
}
Run Code Online (Sandbox Code Playgroud)

我认为错误是正常的,但OK可以是异常的.

所以,我的问题是:

  1. 什么是C++ lambda函数的调用约定?

  2. 如何指定C++ lambda函数的调用约定?

  3. 如果没有定义调用约定,那么在调用lambda函数后如何正确地回收堆栈空间?

  4. 编译器是否自动生成lambda函数的多个版本?即作为以下伪代码:

    [] __stdcall(){};

    [] __cdecl(){}; 等等

yoh*_*hjp 14

在VC++ 2012上,您将"无状态lambda转换为函数指针" ,编译器会选择自动调用无状态lambdas(没有捕获变量)的转换.

MSDN C++ 11特点:

Lambda表达式

[...]此外,在Visual Studio 2012中的Visual C++中,无状态lambda可以转换为函数指针.[...](Visual Studio 2012中的Visual C++甚至比这更好,因为我们已经将无状态lambda转换为具有任意调用约定的函数指针.当你使用期望诸如__stdcall函数指针之类的东西时,这很重要..)


编辑:

注意:调用转换超出了C++标准,它依赖于其他规范,如平台ABI(应用程序二进制接口).

以下答案基于带有/ FAs编译器选项的输出汇编代码.所以这只是一个猜测,请向微软询问更多细节; P.

Q1.什么是C++ lambda函数的调用约定?

Q3.如果没有定义调用约定,那么在调用lambda函数后如何正确地回收堆栈空间?

首先,C++ lambda(-expression)不是函数(也不是函数指针),你可以operator()像调用普通函数一样调用lambda对象.输出汇编代码说VC++ 2012生成带__thiscall调用转换的lambda-body .

Q2.如何指定C++ lambda函数的调用约定?

AFAIK,没有办法.(可能只是__thiscall)

Q4.编译器是否自动生成lambda函数的多个版本?即作为以下伪代码:[...]

大概号的VC++ 2012拉姆达型只提供了一个λ-体实现(void operator()()),但提供多个"用户定义转换为一个函数指针"每个呼叫的转换(带操作员返回函数指针void (__fastcall*)(void),void (__stdcall*)(void)void (__cdecl*)(void)类型).

这是一个例子;

// input source code
auto lm = [](){ /*lambda-body*/ };

// reversed C++ code from VC++2012 output assembly code
class lambda_UNIQUE_HASH {
  void __thiscall operator()() {
    /* lambda-body */
  }
  // user-defined conversions
  typedef void (__fastcall * fp_fastcall_t)();
  typedef void (__stdcall * fp_stdcall_t)();
  typedef void (__cdecl * fp_cdecl_t)();
  operator fp_fastcall_t() { ... }
  operator fp_stdcall_t() { ... }
  operator fp_cdecl_t() { ... }
};
lambda_UNIQUE_HASH lm;
Run Code Online (Sandbox Code Playgroud)

  • @jogojapan:它几乎涵盖了所有内容,因为lambda函数最终仍然是一个成员函数.并且成员函数实际上没有调用约定.不是以`__cdecl`的方式.由于调用约定无论如何都是特定于平台的,因此每个平台都可以决定它的工作方式.微软展示了*令人震惊的能力,决定了最有用的方式. (5认同)
  • +1作为参考(尽管答案并未涵盖问题的所有方面). (2认同)