我有以下初始 C++ 代码:
class Lambda
{
public:
int compute(int &value){
auto get = [&value]() -> int {
return 11 * value;
};
return get();
}
};
int main(){
Lambda lambda;
int value = 77;
return lambda.compute(value);
}
使用 clang 编译(使用 -O1)生成的 ASM 以下:
main: # @main
push rax
mov dword ptr [rsp + 4], 77
mov rdi, rsp
lea rsi, [rsp + 4]
call Lambda::compute(int&)
pop rcx
ret
Lambda::compute(int&): # @Lambda::compute(int&)
push rax
mov qword ptr [rsp], rsi
mov rdi, rsp
call Lambda::compute(int&)::{lambda()#1}::operator()() const
pop rcx
ret
Lambda::compute(int&)::{lambda()#1}::operator()() const: # @Lambda::compute(int&)::{lambda()#1}::operator()() const
mov rax, qword ptr [rdi]
mov eax, dword ptr [rax]
lea ecx, [rax + 4*rax]
lea eax, [rax + 2*rcx]
ret
问题:
- 出现在 ASM 中的
{lambda()#1}
是什么?据我所知,它可能是一个封装函数对象(即 lambda 主体)的闭包。 如果是,请确认。 - 是否每次触发
compute()
时都会生成一个新的闭包?还是同一个实例?
最佳答案
是的,调用 lambda 函数需要生成一个闭包 [除非编译器可以推断它实际上没有被使用]
通过这种优化,每次调用都是对
compute
的调用,后者又会调用内部函数get()
,这是一个 lambda 函数在你的compute
函数中。让编译器优化到更高的程度,对于这种情况,将优化调用 - 在我的尝试中,它将完全删除带有-O2
的整个调用,并且只返回预先计算的常量847 - 如您所料。对于更复杂的情况,它可能会或可能不会内联 lambda 部分但保留外部调用,反之亦然。这在很大程度上取决于所涉及函数内部发生的事情的具体细节。需要说明的是,编译器正在按照您的要求进行操作:调用函数
compute
,后者又调用函数get
。
添加
int value2 = 88;
int tmp = lambda.compute(value2);
进入原始问题中的 main
函数本质上对生成的代码产生了这种变化(在 Linux 上使用 clang++):
main: # @main
pushq %rbx
subq $16, %rsp
movl $77, 12(%rsp)
## new line to set value2
movl $88, 8(%rsp)
movq %rsp, %rbx
## New line, passing reference of `value2` to lambda.compute
leaq 8(%rsp), %rsi
movq %rbx, %rdi
## Call lambda.compute
callq _ZN6Lambda7computeERi
## Same as before.
leaq 12(%rsp), %rsi
movq %rbx, %rdi
callq _ZN6Lambda7computeERi
addq $16, %rsp
popq %rbx
retq
代码生成到
关于C++ Lambda 生成的 ASM 代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49294022/