C++:什么是 R-Value 引用(ASM 技术层面)?

10

可能是重复问题:
什么是右值引用和左值引用之间的区别?(CodeGen)

我想知道,有没有人能够从技术角度解释一下R-Value引用是什么?我的意思是:当创建R-Value引用时,在汇编级别会发生什么。

为了进行内部测试以查看发生了什么,我编写了以下代码:

char c = 255;
char &c2 = c;
char &c3 = std::move(c);

我知道为 'c' 创建一个 R-Value 引用没有意义,但是出于测试的目的我还是这样做了,看看会发生什么。结果如下:

unsigned char c = 255;
    mov         byte ptr [c],0FFh
unsigned char &c2 = c;
    lea         eax,[c]  
    mov         dword ptr [c2],eax 
unsigned char &&c3 = std::move(c);
    lea         eax,[c]  
    push        eax  
    call        std::move<unsigned char &> (0ED1235h)  
    add         esp,4  
    mov         dword ptr [c3],eax

我并不是汇编专家,但在这种情况下,'c3' 看起来是对 'c' 的常规引用。

如果我直接将 R-Value 引用绑定到临时变量(char &&c3 = 255),汇编代码的最后一位会发生以下变化:

unsigned char &&c3 = 255;
    mov         byte ptr [ebp-29h],0FFh  
    lea         eax,[ebp-29h]  
    mov         dword ptr [c3],eax

从这个改变的外观来看,我认为c3仍然实际上是对某个内存位置的引用,该位置保存着值255。因此,它是一个常规的引用-值没有被复制/分配给c3。这是真的吗?
有人能否说出我的假设是否正确,或者我完全错了?直到现在,我总是认为R-Value引用与函数/方法签名(可能是移动构造函数)匹配,当涉及调用解析时,程序员知道如何处理提供的数据(对于移动构造函数,这将是移动数据而不是复制它)。
为了捍卫我刚才提出的相当愚蠢的尝试:我并不打算在汇编级别上搞乱我的代码,我只想理解R-Value引用相对于所有这些年来已经存在的其他引用所引入的技术差异。
任何见解和解释都非常欢迎!
谢谢!

3
在(未优化的)汇编级别上,R-Value 是一个对象的实例,就像 L-Value、引用或“值”一样。 R-Value 只是告诉 编译器 哪些函数用于复制/构造。这类似于对象或参数中的引用在汇编级别上实际上只是指针,但是被编译器以不同的方式处理。 - Mooing Duck
1
请注意,c3 是一个左值引用,而不是右值。 - avakar
3
“技术”这个词,你总是在用。我认为它的含义和你想象中的不一样。 - R. Martinho Fernandes
@Mooning:你所说的“对象实例”究竟是什么意思?我从未听说过“对象”这个词的用法。严格来说,引用是实体。 - fredoverflow
@FredOverflow:从技术上讲是正确的。我避免说“...是一个对象”,因为它似乎不太口语化,但事后看来还是可以的。 - Mooing Duck
4个回答

9
创建 R-Value 引用时会发生什么事情,需要保留高层语义。编译器的具体操作取决于编译器供应商的想法。汇编语言没有 lvalues、rvalues 或引用的概念,因此不要寻找它们。启用优化后,您正在查看的代码可能会发生变化(或者如果变量未使用,则可能停止存在)。
R-Value 引用与多年来已经存在的内容相比引入了技术上的差异。R-Value 引用使移动语义成为可能,并且这些语义又可实现重要的优化机会。标准并没有规定“哦,这些是 rvalue 引用,这就是你应该在汇编中实现它们的方式”。实现甚至可能根本不会生成汇编。

7

Rvalue引用在汇编层面上与普通引用可能完全相同(取决于编译器作为如何处理)。区别仅存在于C++语言层面。 R-value引用所携带的信息是:引用对象是临时的,任何接收它的人都可以自由地修改它。关于对象位置的信息可能会与常规引用完全相同(编译器可能会尝试以不同方式进行优化,但这是编译器的内部问题)。

R-value引用和非const左值引用之间的区别在于,每个左值都将自动转换为左值引用(从而防止意外修改),而r-value表达式将转换为两者(优先使用r-value引用),允许移动语义和常规调用(如果不支持移动语义)。std::move所做的不过是允许将左值非自动地强制转换为r-value引用。


5
在优化之前,引用存在于一个指针中,包含绑定对象的地址。
但编译器会尽力对其进行优化。特别是内联函数可能会导致所有对小函数内引用参数的使用被替换为直接使用寄存器来包含绑定对象的值。

0

rvalue引用的概念可以完全在C++层面上描述,无需阅读汇编代码。您只需要获取一些最小的C++类,该类分配内部资源,并通过另一个对象“窃取”rvalue引用资源是显而易见的。就像这篇经典文章中的remote_integer类一样:http://blogs.msdn.com/b/vcblog/archive/2009/02/03/rvalue-references-c-0x-features-in-vc10-part-2.aspx。这段代码的汇编翻译非常简单,但是差异在于C++代码。对于像char这样的简单类型-它们可以用来演示一些rvalue引用语法特性,但是在C++和汇编级别上使用rvalue引用没有任何意义。因此,如果您在C++中看不到使用char &&c的优势,那么在汇编中也没有什么有趣的东西。


1
嗨,链接丢失了,你能修复一下吗?先谢谢了。 - roachsinai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接