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.
CodePudding user response:
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&)
CodePudding user response:
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.