罐头的两个隐含副本vector
- 并且经常被 - 消除。命名返回值优化可以消除隐含在 return 语句中的副本,return out;
并且也允许消除隐含在副本初始化中的临时性oof
。
在使用这两种优化的情况下,构造vector<foo> out;
的对象与oof
.
使用诸如此类的人工测试用例更容易测试哪些优化正在执行。
struct CopyMe
{
CopyMe();
CopyMe(const CopyMe& x);
CopyMe& operator=(const CopyMe& x);
char data[1024]; // give it some bulk
};
void Mutate(CopyMe&);
CopyMe fn()
{
CopyMe x;
Mutate(x);
return x;
}
int main()
{
CopyMe y = fn();
return 0;
}
复制构造函数已声明但未定义,因此无法内联和消除对它的调用。使用现在相对较旧的 gcc 4.4 进行编译会得到以下程序集-O3 -fno-inline
(过滤以去除 C++ 名称并进行编辑以删除非代码)。
fn():
pushq %rbx
movq %rdi, %rbx
call CopyMe::CopyMe()
movq %rbx, %rdi
call Mutate(CopyMe&)
movq %rbx, %rax
popq %rbx
ret
main:
subq $1032, %rsp
movq %rsp, %rdi
call fn()
xorl %eax, %eax
addq $1032, %rsp
ret
可以看出,没有调用复制构造函数。事实上,gcc 甚至在-O0
. 您必须提供-fno-elide-constructors
关闭此行为;如果你这样做,那么 gcc 会生成两个对复制构造函数的调用CopyMe
- 一个在调用内部,一个在调用外部fn()
。
fn():
movq %rbx, -16(%rsp)
movq %rbp, -8(%rsp)
subq $1048, %rsp
movq %rdi, %rbx
movq %rsp, %rdi
call CopyMe::CopyMe()
movq %rsp, %rdi
call Mutate(CopyMe&)
movq %rsp, %rsi
movq %rbx, %rdi
call CopyMe::CopyMe(CopyMe const&)
movq %rbx, %rax
movq 1040(%rsp), %rbp
movq 1032(%rsp), %rbx
addq $1048, %rsp
ret
main:
pushq %rbx
subq $2048, %rsp
movq %rsp, %rdi
call fn()
leaq 1024(%rsp), %rdi
movq %rsp, %rsi
call CopyMe::CopyMe(CopyMe const&)
xorl %eax, %eax
addq $2048, %rsp
popq %rbx
ret