C/C++学习记录:std::move 源码分析
- 抽空扣一点感兴趣的标准库源码,这里总结一下
std::move()相关的分析 - 本文中 gcc version: 8.4.1 20200928 (Red Hat 8.4.1-1) (GCC)
- 其中c++库安装路径为
/usr/include/c++/8
一、源码与分析
1. std::move 源码总览
std::move() 的定义位于 /usr/include/c++/8/bits/move.h 中,详细内容如下:
可以看到 std::move 的定义只有短短4行…
第二行和第三行关键字 constexpr 和 noexcept 都是c++11的新增关键字。其中 constexpr 把后面的 typename 声明为常量表达式,便于编译器对代码进行优化 。而 noexcept 则声明此函数不会抛出异常,遇到问题直接调用 std::terminate 退出进程。这两个关键字可以说是为规范和优化 std::move 而存在的,对其实现并无参与,所以这里跳过这俩关键字,不做过多分析。
此外还存在两个东西:类型提取结构体 std::remove_reference 和 C++标准转换运算符static_cast 下面单独进行分析。
2. std::remove_reference 源码分析
std::remove_reference 的定义位于 /usr/include/c++/8/type_traits 中,详细内容如下:
可以看到,std::remove_reference 结构体的实现非常简单,功能就是依靠模板把传参 _Tp 的类型分离出来,当调用 std::remove_reference::type 时即为分离出的最底层类型。
- 测试如下内容:

结果如下:
说明std::remove_reference可以很好的将类型提取出来,即int&和int&&都可以提取出基础类型int
3. static_cast 分析
static_cast 也是c++11中的新特性,简单来说用处就是类型转换。语法为:
1 | static_cast<新类型>(表达式) |
其返回值为 “新类型” 类型的值,例如 n = static_cast<int>(3.14) 后,此时 n = 3。我个人认为可以粗略的将 static_cast 当作一个更高级的强制类型转换,相比传统的强制类型转换,static_cast 会对转换类型进行检测,所以相对更加安全。
可以说,任何具有明确定义的类型转换,只要不包含底层const,都可以使用 static_cast。
4. std::move 分析
由上文可知两个关键内容的作用,则可首先带入一个实例来化简分析 std::move 的实际作用。
首先是一小部分代码:
1 | std::string s = "Hello"; |
根据 std::move 的流程,std::move(s) 中的 return 语句执行过程如下:
1 | string&& move(string& && t) //此处string& &&等于string&,下文会提及 |
所以,某种意义上来说,std::move(lvalue) 就约等于 static_cast<T&&>(lvalue),即将左值强制转换为右值。而 std::move 中封装了一个类型提取器 std::remove_reference 来方便使用。
5. std::move 中的引用折叠
通过上文的内容,可以发现 std::move 中的传参类型为 _Tp&& ,如下:
1 | template<typename _Tp> |
那么,在执行过程中,传参s的类型是什么呢?
由于传参类型为 _Tp&&,那么当传参类型为 string&(即左值) 时,当时的场景则为:
1 | std::string s = "Hello"; |
此时的类型 string& && 又是什么?此处便涉及到了引用折叠。概念如下,简单来说就是除了右值的&&是右值,其他都是左值。
- X& &、X&& &、X& && —— 折叠成X&,用于处理左值
- X&& && —— 折叠成X&&,用于处理右值
引用折叠的意义就是让参数可以与任何类型的实参匹配,简单说就是右值传进来还是右值,左值传进来还是左值。例如上文传进来的就是左值,最后还是左值;如果传进来的是右值则最终还是右值。另外我粗略看了下 forward 的源码,其实它实现完美转发也是很大程度依赖于引用折叠这个东西。
二、总结
C++的标准库源码依旧封装的很”繁琐”,以及配着贼长的命名。当然 std::move 这个函数还好,涉及的东西不算太多,所以看着还是非常清晰的。之前看智能指针源码才是真的给我看的烦躁无比。
小结一下,std::move 中只进行了一个类型转换,而各种所谓右值数据迁移基本都是在构造函数中实现的。
总的来说标准库里的源码写的还是相对很严谨和标准的,很多思路和写法确实能让我学到很多。接下来我准备再去仔细研究一下 forward 的实现和思路。当然 std::move 里那个最关键的 static_cast 我还是没有深入的探索,只是浅尝辄止,可能等未来实力和精力足够再来一探究竟吧。