c++11的 move并没有实际move
c++11的 move并没有实际move
首先看第一个例子:
1 #include <iostream > // std::cout 2 #include <utility > // std::move 3 #include <vector > // std::vector 4 #include <string > // std::string 5 6 int main() { 7 8 std::string str = "Hello world."; 9 std::vector <std::string > v; 10 11 // 将 使 用 push_back(const T&), 即 产 生 拷 贝 行 为 12 v.push_back(str); 13 // 将 输 出 "str: Hello world." 14 std::cout << "str: " << str << std::endl; 15 16 // 将 使 用 push_back(const T&&), 不 会 出 现 拷 贝 行 为 17 // 而 整 个 字 符 串 会 被 移 动 到 vector 中, 所 以 有 时 候 std::move 会 用 来 减 少 拷 贝 出现 的 开 销 18 // 这 步 操 作 后 , str 中 的 值 会 变 为 空 19 v.push_back(std::move(str)); 20 // 将 输 出 "str: " 21 std::cout << "str: " << str << std::endl; 22 23 return 0; 24 }
最后打印为”str: “,和我一开始理解的move语义一致,str被移入了vector中,所以str为空。
再看下一段代码,这是在讲完美转发时的一个例子:
1 # include <iostream > 2 # include <utility > 3 void reference ( int & v) { 4 std :: cout << " 左 值 引 用 " << std :: endl ; 5 } 6 void reference ( int && v) { 7 std :: cout << " 右 值 引 用 " << std :: endl ; 8 } 9 template <typename T> 10 void pass (T&& v) { 11 std :: cout << " 普 通 传 参 :"; 12 reference (v); 13 std :: cout << " std :: move 传 参 :"; 14 reference (std :: move (v)); 15 std :: cout << " std :: forward 传 参 :"; 16 reference (std :: forward <T >(v)); 17 } 18 19 int main () { 20 std :: cout << " 传 递 右 值 :" << std :: endl ; 21 pass (1); 22 23 std :: cout << " 传 递 左 值 :" << std :: endl ; 24 int v = 1; 25 pass (v); 26 27 return 0; 28 }
输出结果为:
传 递 右 值 :
普 通 传 参 : 左 值 引 用
std :: move 传 参 : 右 值 引 用
std :: forward 传 参 : 右 值 引 用
传 递 左 值 :
普 通 传 参 : 左 值 引 用
std :: move 传 参 : 右 值 引 用
std :: forward 传 参 : 左 值 引 用
令我困惑的是,在pass中,v明明先调用了reference (std :: move (v));然后又调用了一次reference (std :: forward <T >(v));
那么v不应该在第一次move后就已经被移走了吗?
答案是no。
我们看一下move的实现:
1 template <class _Ty> 2 _NODISCARD constexpr remove_reference_t<_Ty>&& move(_Ty&& _Arg) noexcept { // forward _Arg as movable 3 return static_cast<remove_reference_t<_Ty>&&>(_Arg); 4 }
也就是说move仅仅只是做了个类型转换,将其转为右值引用类型,并未实际发生移动效果。
而前面第一个例子中的push_back中,实际触发了string的移动构造,导致str被真正移走。
所以,为验证这个想法,只需要简单修改第二个例子就可以验证:
1 # include <iostream > 2 # include <utility > 3 int reference(int& v) { 4 std::cout << " 左 值 引 用 " << std::endl; 5 return v; 6 } 7 int reference(int&& v) { 8 std::cout << " 右 值 引 用 " << std::endl; 9 return std::move(v); 10 } 11 12 template <typename T> 13 void pass(T&& v) { 14 std::cout << " 普 通 传 参 :"; 15 reference(v); 16 std::cout << " std :: move 传 参 :"; 17 int a= reference(std::move(v)); 18 std::cout << " std :: forward 传 参 :"; 19 int b = reference(std::forward <T >(v)); 20 } 21 22 int main() { 23 std::cout << " 传 递 右 值 :" << std::endl; 24 pass(1); 25 26 std::cout << " 传 递 左 值 :" << std::endl; 27 int v = 1; 28 pass(v); 29 30 return 0; 31 }
pass(1)时,v未被移走,而用下面int v = 1传递时,在执行完int a= reference(std::move(v));之后,v就已经不存在了,此处才发生了真正的移动操作。