为什么 std::move 在这里生成指令?

Why is std::move generating instructions here?

我多次听说 std::move(t) 或多或少只是 static_cast<T&&>(t) 的一种奇特表达方式,不会生成任何指令。 当我现在在 godbolt 上玩 std::move 为了更好地理解移动语义时,我看到它 确实 (或至少可能)生成指令。在这个例子中

#include <iostream>
using namespace std;

struct S {
    S() { cout << "default ctor" << endl; }
    S(S&& s) {
        i = s.i;
        s.i = 0;
        cout << "move ctor" << endl;
    }
    int i;
};

void foo(S s) { cout << "Foo called with " << s.i << endl; }

int main() {
    S s;
    foo(static_cast<S&&>(s));
    foo(std::move(s));
}

foo 的调用导致以下汇编输出

        ; ... snip ...
        lea     rdi, [rbp - 16]
        lea     rsi, [rbp - 8]
        call    S::S(S&&) [base object constructor]
        lea     rdi, [rbp - 16]
        call    foo(S)
        lea     rdi, [rbp - 8]
        call    std::remove_reference<S&>::type&& std::move<S&>(S&)
        lea     rdi, [rbp - 24]
        mov     rsi, rax
        call    S::S(S&&) [base object constructor]
        lea     rdi, [rbp - 24]
        call    foo(S)
        ; ... snip ...
std::remove_reference<S&>::type&& std::move<S&>(S&): # @std::remove_reference<S&>::type&& std::move<S&>(S&)
        push    rbp
        mov     rbp, rsp
        mov     qword ptr [rbp - 8], rdi
        mov     rax, qword ptr [rbp - 8]
        pop     rbp
        ret

有人可以给我解释一下吗?我不太明白这个 std::remove_reference<S&>::type&& std::move<S&>(S&) 函数应该做什么,以及为什么与通常所说的有明显的收缩。

至于std::remove_reference<S&>::type&& std::move<S&>(S&) Josuttis 在他的 C++ 移动语义中解释了这一点。

基本上它所做的与static_cast<T&&>类似,但具有类型特征的方式。它允许传入任何值类别(因此,左值或右值引用)然后它切断引用部分并应用于 rvalue-ref 一个。至于额外的说明:任何优化器都应该内联这些调用。

关闭优化并将 foo 定义为 void foo(const S& s); 以减少噪音:

foo(static_cast<S&&>(s));

        leaq    -4(%rbp), %rax
        movq    %rax, %rdi
        call    foo(S const&)

foo(std::move(s));

        leaq    -4(%rbp), %rax
        movq    %rax, %rdi
        call    std::remove_reference<S&>::type&& std::move<S&>(S&)
        movq    %rax, %rdi

-O1 的结果相同:

        leaq    12(%rsp), %rdi
        call    foo(S const&)

您正在编译时未进行优化。因此,您可以准确地看到所写的内容,而无需任何简化或内联函数的尝试。

生成的代码大致等同于 type&& foo(type& x) { return x; } 生成的代码,这就是 move 所做的。

研究在没有打开优化的情况下生成的程序集是徒劳的。