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

发布于 2025-01-20 22:57:42 字数 1801 浏览 0 评论 0原文

我一次又一次听到std :: move(t)或多或少是一种奇特的说法static_cast< t&&>(t)没有生成任何说明。 当我现在使用std :: Move在Godbolt上玩耍时,为了更好地理解移动语义,我看到它 dos (或至少可以)生成说明。在此示例中,

#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&lt; s&amp; :: type&amp;&amp;&amp; std :: move&lt; s&amp;&gt;(s&amp;)函数应该做,以及为什么对通常所讲的内容有明显的收缩。

I heard time and again that std::move(t) is more or less only a fancy way of saying static_cast<T&&>(t) and would not generate any instructions.
When I was playing around now with std::move on godbolt in order to better understand move semantics I saw that it does (or at least may) generate instructions. In this example

#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));
}

the calls to foo lead to the following assembly output

        ; ... 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

Can someone please explain this to me? I can't much sense of what this std::remove_reference<S&>::type&& std::move<S&>(S&) function is supposed to do and why there is this apparent contraction to what's commonly told.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

泅人 2025-01-27 22:57:42

至于std :: remove_reference&lt; s&amp; :: type&amp;&amp; std :: move&lt; s&amp;&gt;(s&amp;)
Josuttis在他的C ++移动语义中解释了这一点。

基本上,它的作用类似于static_cast&lt; t&amp;&amp;&gt;,但具有类型特征的方式。它允许将任何值类别传递到中(因此,lvalue或rvalue参考),然后切断参考部分并适用于rvalue-ref One。至于额外的说明:任何优化器都应将这些呼叫内联。

关闭优化和定义为void foo(const s&amp; s);降低噪声:

foo(static_cast&lt; s&amp;&amp;&amp;&gt;(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&)

As for std::remove_reference<S&>::type&& std::move<S&>(S&)
Josuttis explains this in his C++ move semantics.

Basically what it does is similar to static_cast<T&&> but with the means of type traits. It allows any value category to be passed in (thus, either lvalue or rvalue reference) then it cuts off the reference part and applies to rvalue-ref one. As for the extra instructions: any optimizer should inline those calls anyway.

With optimization turned off and foo defined as void foo(const S& s); to reduce noise:

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

With -O1, both result in the same:

        leaq    12(%rsp), %rdi
        call    foo(S const&)
じ违心 2025-01-27 22:57:42

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

生成的代码大致相当于 type&& foo(type&x) { 返回 x; } 会生成,这就是 move 的作用。

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

You are compiling without optimisations. So you see exactly what is written without any attempt to simplify or inline functions.

Generated code is roughly eqivalent to what type&& foo(type& x) { return x; } would generate, which is what move does.

Studying assembly generated without optimisations turned on is exercise in futility.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文