考虑foo
函数
void foo(X x);
X
矩阵cass和函数
X foobar();
假设我跑
foo(foobar());
在这种情况下,临时对象一步一步会发生什么?我的理解是
foobar
会返回一个临时对象,例如Xtemp1
; foo
将Xtemp1
复制到自己的临时对象,比如Xtemp2
,然后销毁Xtemp1
; foo
对Xtemp2
执行计算。另一方面,如果我将foo
重载为
void foo(X& x);
void foo(X&& x);
然后图片会有所不同,特别是
foobar
返回临时Xtemp1
; foo
不会创建新的临时文件,而是通过其引用直接作用于Xtemp1
。这张照片是否正确,如果没有,有人可以指出并修正我的错误吗?非常感谢你。
答案 0 :(得分:2)
foo(foobar());
foobar
的返回值是一个值,因此它是一个临时值。foo
的参数中。foo
的调用执行。foo
返回时,它的参数被破坏。foo
返回时,本地存储中的值将被破坏。foobar
的返回值被破坏。1 编译器可以省略这些移动/复制。这意味着不必发生这些复制/移动(任何值得使用的编译器都会忽略它们。)
2 如果省略上面的移动/复制,则自然不需要销毁它们各自的变量。因为它们不存在。
另一方面,如果我将foo重载为
foobar
的返回值是一个值,因此它是一个临时值。foo(X &&)
进行呼叫。foo
的调用执行。foo
返回时,它的引用参数被破坏(不是它引用的值)。foo
返回时,本地存储中的值将被破坏。foobar
的返回值被破坏。请注意这里的主要区别。第4步和第6步不能被删除。因此,如果X
是一个像int
这样的小类型,那么该函数别无选择,只能创建一个对整数毫无价值的引用。引用在内部实现为指针,因此编译器实际上不可能仅将其作为寄存器进行优化。因此,本地存储必须是在堆栈而不是寄存器。
因此,您将获得更少的移动/复制。但同样,任何体面的编译器都会忽视它们。所以问题是,X
通常是否太大而无法放入寄存器?
答案 1 :(得分:1)
您的理解几乎是正确的。唯一的区别是,在步骤2中,临时Xtemp1
不会复制到任意命名的临时Xtemp2
,而是复制到形式参数x
的空间(来自声明{{ 1}})。
此外,copy-elision可能会启动,这可能意味着foo(X x)
的返回值直接在foobar()
的形式参数foo
的空间中构建,因此不会发生复制。这是标准允许的,但不能保证。
您对r值参考案例的看法是正确的。