向量
的两个隐含副本都可以而且经常被消除。命名返回值优化可以消除return语句<code>return out中隐含的副本的复制初始化中隐含的for the temporary也可以被消除。
在两种优化都在进行的情况下,在<code>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构造函数
才能关闭此行为;如果这样做,那么gcc会生成两个对<code>CopyMe</code>的复制构造函数的调用——一个在对<code<fn()</code<的调用内部,一个在调用外部。
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