以下代码是使用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
}
我认为错误是正常的,但确定不正常。
所以,我的问题是:
C ++ lambda函数的调用约定是什么?
如何指定C ++ lambda函数的调用约定?
如果没有定义调用约定,如何在调用lambda函数后正确地回收堆栈空间?
编译器是否自动生成lambda函数的多个版本?即作为以下伪代码:
[] __stdcall(){};
[] __cdecl(){};等
答案 0 :(得分:14)
在VC ++ 2012上,编译器选择自动调用无状态lambda的转换(没有捕获变量)当将“无状态lambda转换为函数指针”时。
<强> lambda表达式强>
[...]此外,在Visual Studio 2012中的Visual C ++中,无状态lambda可以转换为函数指针。 [...](Visual Studio 2012中的Visual C ++甚至比这更好,因为我们已经将无状态lambda转换为具有任意调用约定的函数指针。当你使用期望像{{{{{{{ 1}}函数指针。)
<强>编辑:强>
NB:调用转换不在C ++ Standard中,它取决于其他规范,如平台ABI(应用程序二进制接口)。
以下答案基于/FAs compiler option的输出汇编代码。 所以这只是一个猜测,请向微软询问更多细节; P
Q1。什么是C ++ lambda函数的调用约定?
Q3。如果没有定义调用约定,那么在调用lambda函数后如何正确地回收堆栈空间?
首先,C ++ lambda(-expression)不是函数(也不是函数指针),你可以像调用普通函数一样调用__stdcall
到lambda对象。
输出汇编代码表示VC ++ 2012生成带有operator()
调用转换的lambda-body。
Q2。如何指定C ++ lambda函数的调用约定?
AFAIK,没有办法。 (可能只有__thiscall
)
Q4。编译器是否自动生成lambda函数的多个版本?即作为以下伪代码:[...]
可能不会。
VC ++ 2012 lambda类型只提供一个lambda-body实现(__thiscall
),但为每个调用转换提供了多个“用户定义的转换到函数指针”(带void operator()()
的运算符返回函数指针,{ {1}}和void (__fastcall*)(void)
类型)。
这是一个例子;
void (__stdcall*)(void)
答案 1 :(得分:3)
无状态lambda函数仍然是一个类,但是可以隐式转换为函数指针的类。
C ++标准不包括调用约定,但是没有理由说无状态lambda无法在任何调用约定中创建包装器,当lambda转换为函数指针时,该约定转发到无状态lambda。 p>
例如,我们可以这样做:
#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
无状态nullary类可以隐式转换为cdecl
和stdcall
函数指针。
这一点的重要部分是调用约定是函数指针类型的一部分,因此operator function_type
知道正在请求的调用约定。通过完美的转发,上述甚至可以有效。