政府网站回复集镇建设规划/软文写作服务
支持移动语义,是现代C++的主要语言特性之一,std::move本质上就是把一个变量强转成右值引用。在gcc的源码中,std::move的实现如下:
template<typename _Tp>_GLIBCXX_NODISCARDconstexpr typename std::remove_reference<_Tp>::type&&move(_Tp&& __t) noexcept{ return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
通常,我们在程序中使用std::move都是为了触发移动构造函数或移动赋值运算符的调用,举个例子:
#include <string>int main()
{std::string str = "123";std::string str2 = std::move(str);return 0;
}
显然,通过std::move触发了移动构造函数。
如果等号的右侧是一个临时对象,则不需要调用std::move,这种场景,编译器会自动触发移动操作:
#include <string>int main()
{std::string str;str = std::string("123");return 0;
}
通过对应的汇编代码,我们确认,调用了std::string的移动赋值运算符:
.LC0:.string "123"
main:stp x29, x30, [sp, -112]!mov x29, spstr x19, [sp, 16]add x0, sp, 32bl std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string() [complete object constructor]add x0, sp, 96str x0, [sp, 104]nopnopadd x0, sp, 96add x3, sp, 64mov x2, x0adrp x0, .LC0add x1, x0, :lo12:.LC0mov x0, x3bl std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) [complete object constructor]add x1, sp, 64add x0, sp, 32bl std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::operator=(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&)
原因其实也容易理解,临时对象就是右值,编译器对于右值的处理方式,就是调用移动构造函数或者移动赋值运算符。
如果一个函数的传入参数是常量,则调用std::move将无法触发移动构造:
#include <string>int main()
{const std::string str = "123";std::string str2 = std::move(str);return 0;
}
这种场景下,编译器生成代码实际上调用的是std::string的拷贝构造函数:
.LC0:.string "123"
main:stp x29, x30, [sp, -112]!mov x29, spstr x19, [sp, 16]add x0, sp, 96str x0, [sp, 104]nopnopadd x0, sp, 96add x3, sp, 64mov x2, x0adrp x0, .LC0add x1, x0, :lo12:.LC0mov x0, x3bl std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) [complete object constructor]add x0, sp, 96bl std::__new_allocator<char>::~__new_allocator() [base object destructor]nopadd x0, sp, 64bl std::remove_reference<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&>::type&& std::move<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)mov x1, x0add x0, sp, 32bl std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) [complete object constructor]mov w19, 0add x0, sp, 32bl std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [complete object destructor]add x0, sp, 64bl std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [complete object destructor]mov w0, w19b .L7mov x19, x0add x0, sp, 96bl std::__new_allocator<char>::~__new_allocator() [base object destructor]nopmov x0, x19bl _Unwind_Resumemov x19, x0add x0, sp, 64bl std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() [complete object destructor]mov x0, x19bl _Unwind_Resume
.L7:ldr x19, [sp, 16]ldp x29, x30, [sp], 112ret
DW.ref.__gxx_personality_v0:.xword __gxx_personality_v0
这个也容易理解,移动构造函数可能会修改入参,因为定义为常量,不能修改,所以不能调用移动构造函数,但是拷贝构造函数的入参是可以接受常量的。实际上std::move的类型强转是成功的,转型的结果就是常量右值引用:
#include <string>
#include <iostream>template<typename T> void f(T&& param) // universal reference
{using ParamType = T&&;bool isCnstRValRef = std::is_same<ParamType, const std::string&&>::value;if (isCnstRValRef) {std::cout << "param's type is const std::string&&\n";} else {std::cout << "param's type is other type\n";}
}int main()
{const std::string str = "123";f(std::move(str));std::string str2 = std::move(str);return 0;
}
测试结果就是const std::string&&:
[root@192 moderncpp]# ./test_move
param's type is const std::string&&
总结一下:
使用std::move就是为了把入参强转为右值,通常这样做是为了触发移动构造函数的调用,大部分场景都是OK的,但是,如果入参是常量,不要加std::move,因为加了也不会调用移动构造函数;如果入参是临时对象,也不要加std::move,因为不加也会调用移动构造函数。
参考资料:
《Effective Modern C++》
《C++并发编程实战》