c++ 的 const reference extend lifetime rvalue
c++ 中,如果一个函数返回一个对象,那么这个对象是如果返回的呢?
本文用 OSX 下的 llvm 来汇编理解这个定义。
// life_expansion.cpp
#include <iostream>
#include <functional>
#include <boost/type_index.hpp>
using namespace std;
class Foo {
public:
Foo(int v):i(v) {
cout << __FILE__ << ":" << __LINE__ << ": [" << __PRETTY_FUNCTION__<< "] "
<< "this " << this << " "
<< "i " << i << " "
<< endl;
}
Foo(const Foo& foo):i(foo.i + 1) {
cout << __FILE__ << ":" << __LINE__ << ": [" << __PRETTY_FUNCTION__<< "] "
<< "i " << i << " "
<< endl;
}
~Foo() {
cout << __FILE__ << ":" << __LINE__ << ": [" << __PRETTY_FUNCTION__<< "] "
<< "i " << i << " "
<< "this " << this << " "
<< endl;
i = -100;
}
public:
int i = 0;
};
Foo calc()
{
Foo foo(1);
return foo;
}
void show(const Foo& f){
printf("%d\nq", f.i);
}
int main(int argc, char *argv[])
{
printf("before\n");
{
const auto& f1 = calc();
show(f1);
}
printf("after\n");
return 0;
}
我们看一下,产生的汇编
% c++ -O3 -fno-inline -std=c++11 -o a.out life_expansion.cpp
otool -tV a.out | c++filt
截取其中的汇编代码
_main:
pushq %rbp
movq %rsp, %rbp
pushq %rbx
pushq %rax
leaq 0x869(%rip), %rdi ## literal pool for: "before"
callq 0x100000d10 ## symbol stub for: _puts # 这里调用 printf
leaq -0x10(%rbp), %rbx # 这里申请临时变量的内存
movq %rbx, %rdi
callq calc() ## calc()
movq %rbx, %rdi
callq show(Foo const&) ## show(Foo const&)
movq %rbx, %rdi
callq Foo::~Foo() ## Foo::~Foo()
leaq 0x848(%rip), %rdi ## literal pool for: "after"
callq 0x100000d10 ## symbol stub for: _puts
xorl %eax, %eax
addq $0x8, %rsp
popq %rbx
popq %rbp
retq
nopw %cs:(%rax,%rax)
calc():
pushq %rbp
movq %rsp, %rbp
pushq %rbx
pushq %rax
movq %rdi, %rbx
movl $0x1, %esi
callq Foo::Foo(int) ## Foo::Foo(int)
movq %rbx, %rax
addq $0x8, %rsp
popq %rbx
popq %rbp
retq
nopl (%rax)
这里可以看到,对于 calc
的返回值,实际分配内存是在调用者的堆栈里申请的。
而如果调用者,用一个 const reference 抓住(引用) 这个变量的话,那么这个临时变量的生命周期会被拉长。
他的生命周期拉长到和 const reference 的生命周期一样。
详见 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/1993/N0345.pdf