首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >C++ lambda函数的默认调用约定是什么?

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

Stack Overflow用户
提问于 2013-02-13 11:00:09
回答 2查看 4.8K关注 0票数 16

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

代码语言:javascript
运行
复制
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
}

我认为错误是正常的,但OKs是不正常的。

所以,我的问题是:

  1. C++ lambda函数的调用约定是什么?
  2. 如何指定C++ lambda函数的调用约定?
  3. 如果没有定义调用约定,如何在调用lambda函数后正确地回收堆栈空间?
  4. 编译器是否自动生成lambda函数的多个版本?即下面的伪代码:

[] __stdcall (){};

[] __cdecl (){};等。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-02-13 11:41:54

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

MSDN C++11 Features

Lambdas

..。此外,在Visual Studio2012中的Visual C++中,无状态lambda可以转换为函数指针。...

编辑:

NB:调用转换不符合C++标准,它依赖于其他规范,如平台ABI(应用程序二进制接口)。

以下答案基于使用/FAs compiler option输出的汇编代码。因此,这只是一个猜测,请向微软了解更多细节;

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

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

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

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

阿法克,没有办法。(可能只有__thiscall)

Q4.编译器会自动生成lambda函数的多个版本吗?即下面的伪代码:...

可能不是。VC++ 2012 lambda-type只提供了一个lambda-body实现(void operator()()),但是为每个调用转换(具有void (__fastcall*)(void)void (__stdcall*)(void)void (__cdecl*)(void)类型的运算符返回函数指针)提供了多个“用户定义的函数指针转换”。

下面是一个例子;

代码语言:javascript
运行
复制
// 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;
票数 15
EN

Stack Overflow用户

发布于 2013-02-13 11:43:37

无状态的lambda函数仍然是一个类,但它是一个可以隐式转换为函数指针的类。

C++标准没有涵盖调用约定,但是在将lambda转换为函数指针时,无状态lambda不能在任何调用约定中创建包装器以转发到无状态lambda,这是没有道理的。

举个例子,我们可以这样做:

代码语言:javascript
运行
复制
#include <iostream>

void __cdecl h1() {}
void __stdcall h2(){}

// I'm lazy: 
typedef decltype(&h1) cdecl_nullary_ptr;
typedef decltype(&h2) stdcall_nullary_ptr;

template<typename StatelessNullaryFunctor>
struct make_cdecl {
  static void __cdecl do_it() {
    StatelessNullaryFunctor()();
  }
};
template<typename StatelessNullaryFunctor>
struct make_stdcall {
  static void __stdcall do_it() {
    StatelessNullaryFunctor()();
  }
};

struct test {
  void operator()() const { hidden_implementation(); }

  operator cdecl_nullary_ptr() const {
    return &make_cdecl<test>::do_it;
  }
  operator stdcall_nullary_ptr() const {
    return &make_stdcall<test>::do_it;
  }
};

其中我们的test无状态空类可以隐式地转换为cdeclstdcall函数指针。

其中很重要的一点是,调用约定是函数指针类型的一部分,因此operator function_type知道所请求的调用约定。有了完美的转发,上面的内容甚至可以是高效的。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14845706

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档