Rvalue Reference 右值引用
当赋值操作的右边是右值(rvalue),左边的对象不需要特意分配内存去存放这个拷贝(copy),而可以搬移(move)右边对象的资源。
用于解决不必要的拷贝和实现完美转发(perfect forwarding)。
Move Semantics 移动语义
move 相当于 浅拷贝 + 打断原指针,原来的对象无法再使用。
STL 许多地方使用到了右值引用和 move 语义,如 vector 中的 insert() 函数
iterator insert(const_iterator pos, const value_type& x); iterator insert(const_iterator pos, const value_type&& x) // 接受右值引用 { return emplace(pos, std::move(x)); } // 将左值变量放到std::move()中,就取得了它的右值引用
#include<iostream> #include<string> using namespace std; template<typename T1, typename T2> void set(T1 && var1, T2 && var2){ T1 m_var1 = std::forward<T1>(var1); T2 m_var2 = std::forward<T2>(var2); } void set2(string && var1, string && var2){ } void set3(const string & var1, const string & var2){ } int main() { string str1("hello"); string str2("world"); set(str1, str2); set("temporary str1","temporary str2"); set3("temporary str1","temporary str2"); //set2(str1, str2); return 0; }
编译没问题
root@ubuntu:~/c++# vi rvalue3.cpp +21 #include<iostream> #include<string> using namespace std; template<typename T1, typename T2> void set(T1 && var1, T2 && var2){ T1 m_var1 = std::forward<T1>(var1); T2 m_var2 = std::forward<T2>(var2); } void set2(string && var1, string && var2){ } void set3(const string & var1, const string & var2){ } void set4(string & var1, string & var2){ } int main() { string str1("hello"); string str2("world"); set(str1, str2); set("temporary str1","temporary str2"); set3("temporary str1","temporary str2"); set4("temporary str1","temporary str2"); //set2(str1, str2); return 0; }
value3.cpp: In function ‘int main()’: rvalue3.cpp:24:40: error: invalid initialization of non-const reference of type ‘std::__cxx11::string& {aka std::__cxx11::basic_string<char>&}’ from an rvalue of type ‘std::__cxx11::string {aka std::__cxx11::basic_string<char>}’ set4("temporary str1","temporary str2"); ^ In file included from /usr/include/c++/5/string:52:0, from /usr/include/c++/5/bits/locale_classes.h:40, from /usr/include/c++/5/bits/ios_base.h:41, from /usr/include/c++/5/ios:42, from /usr/include/c++/5/ostream:38, from /usr/include/c++/5/iostream:39, from rvalue3.cpp:1: /usr/include/c++/5/bits/basic_string.h:455:7: note: after user-defined conversion: std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>] basic_string(const _CharT* __s, const _Alloc& __a = _Alloc()) ^ rvalue3.cpp:15:6: note: initializing argument 1 of ‘void set4(std::__cxx11::string&, std::__cxx11::string&)’ void set4(string & var1, string & var2){ ^
#include<iostream> using namespace std; void process(int& i) { cout << "process(int&): " << i << endl; } void process(int&& i) { cout << "process(int&&): " << i << endl; } void forward(int&& i) { cout << "forward(int&&): " << i << ", "; process(i); } int main() { int a = 0; process(a); process(1); process(move(a)); forward(2); forward(move(a)); //forward(a); return 0; }
process(int&): 0 process(int&&): 1 process(int&&): 0 forward(int&&): 2, process(int&): 2 forward(int&&): 0, process(int&): 0
// Rvalue经由forward()传给另一个函数却变为Lvalue,原因是传递过程中它变成了named object
#include<iostream> using namespace std; void process(int& i) { cout << "process(int&): " << i << endl; } void process(int&& i) { cout << "process(int&&): " << i << endl; } void forward(int&& i) { cout << "forward(int&&): " << i << ", "; process(i); } int main() { int a=0; forward(a); return 0; }
forward.cpp: In function ‘int main()’: forward.cpp:21:14: error: no matching function for call to ‘forward(int&)’ forward(a); ^ forward.cpp:14:6: note: candidate: void forward(int&&) <near match> void forward(int&& i) { ^ forward.cpp:14:6: note: conversion of argument 1 would be ill-formed: forward.cpp:21:14: error: cannot bind ‘int’ lvalue to ‘int&&’ forward(a); ^ In file included from /usr/include/c++/5/bits/stl_pair.h:59:0, from /usr/include/c++/5/bits/stl_algobase.h:64, from /usr/include/c++/5/bits/char_traits.h:39, from /usr/include/c++/5/ios:40, from /usr/include/c++/5/ostream:38, from /usr/include/c++/5/iostream:39, from forward.cpp:1: /usr/include/c++/5/bits/move.h:87:5: note: candidate: template<class _Tp> constexpr _Tp&& std::forward(typename std::remove_reference<_From>::type&&) forward(typename std::remove_reference<_Tp>::type&& __t) noexcept ^ /usr/include/c++/5/bits/move.h:87:5: note: template argument deduction/substitution failed: forward.cpp:21:14: note: couldn't deduce template parameter ‘_Tp’ forward(a); ^ In file included from /usr/include/c++/5/bits/stl_pair.h:59:0, from /usr/include/c++/5/bits/stl_algobase.h:64, from /usr/include/c++/5/bits/char_traits.h:39, from /usr/include/c++/5/ios:40, from /usr/include/c++/5/ostream:38, from /usr/include/c++/5/iostream:39, from forward.cpp:1: /usr/include/c++/5/bits/move.h:76:5: note: candidate: template<class _Tp> constexpr _Tp&& std::forward(typename std::remove_reference<_From>::type&) forward(typename std::remove_reference<_Tp>::type& __t) noexcept ^ /usr/include/c++/5/bits/move.h:76:5: note: template argument deduction/substitution failed: forward.cpp:21:14: note: couldn't deduce template parameter ‘_Tp’ forward(a); ^
使用 std::forward<T>(),保留参数的左/右值特性。
#include<iostream> using namespace std; void process(int& i) { cout << "process(int&): " << i << endl; } void process(int&& i) { cout << "process(int&&): " << i << endl; } void forward(int&& i) { cout << "forward(int&&): " << i << ", "; process(std::forward<int>(i)); } int main() { int a=0; forward(move(a)); forward(2); return 0; }
root@ubuntu:~/c++# g++ -std=c++11 forward.cpp -o forward root@ubuntu:~/c++# ./forward forward(int&&): 0, process(int&&): 0 forward(int&&): 2, process(int&&): 2