std::move 之后,t到底是什么类型?
先看这段代码:
std::string s="hello";std::string t=std::move(s);运行后,t是std::string&&吗?
结论
t的类型就是std::string,不是std::string&&。
关键是要把「表达式的类型」和「变量的类型」分开来看。
逐步拆解
1.std::move(s)只是一个类型转换
std::move(s)等价于static_cast<std::string&&>(s),作用是把具名变量s从 lvalue 转成 xvalue。
这个表达式的类型确实是std::string&&,但它在运行时什么都不做,不会移动任何东西。
2. 真正的移动发生在初始化t时
编译器会匹配到std::string的移动构造函数(形参类型为std::string&&),并用它来构造t。当字符串较长时,t会直接接管s持有的堆内存指针。
3.t的类型由声明决定
t的声明类型由等号左边决定,即std::string。
此外,C++ 中所有具名变量都是 lvalue,哪怕它是通过移动构造出来的。所以t本身是 lvalue,以后想再把t的内容移走,还得写std::move(t)。
用decltype验证
#include<string>#include<type_traits>intmain(){std::string s="hello";std::string t=std::move(s);static_assert(std::is_same_v<decltype(t),std::string>);// t 是 std::stringstatic_assert(std::is_same_v<decltype(std::move(s)),std::string&&>);// 表达式 move(s) 才是 &&static_assert(std::is_same_v<decltype((t)),std::string&>);// 给 t 加括号得到 lvalue 引用}用g++ -std=c++17或clang++ -std=c++17编译,三条断言都能通过。
小结
| 对象 | 类型 | 值类别 |
|---|---|---|
std::move(s) | std::string&& | xvalue |
t | std::string | lvalue |
(t) | std::string&(decltype结果) | lvalue |
补充:移动后的s是什么状态?
移动之后,标准只保证s处于「有效但未指定」的状态,不保证它为空。
libstdc++ 和 libc++ 通常会把它清空,但这属于实现行为,依赖它一定为空是不可移植的做法。移动后的s只应该被赋新值、销毁,或调用没有前置条件的操作(如clear())。