Modern C++ 学习笔记 —— 右值、移动篇

往期精彩:

  • Modern C++ 学习笔记——易用性改进篇
  • Modern C++ 学习笔记 —— 右值、移动篇
  • Modern C++ 学习笔记 —— 智能指针篇
  • Modern C++ 学习笔记 —— lambda表达式篇
  • Modern C++ 学习笔记 —— C++面向对象编程
  • Modern C++ 学习笔记 —— C++函数式编程

Modern C++ 学习笔记 – 右值、移动篇

关键字:右值,移动,万能引用,完美转发

文章目录

  • Modern C++ 学习笔记 -- 右值、移动篇
    • 引入
    • 右值和移动究竟解决了什么问题?
      • 值类别
      • 移动
    • 不要返回本地变量的引用
    • 生命周期延长
    • 万能引用
    • 引用折叠
    • 到底应不应该返回对象
    • 参考资料

引入

先看如下代码:

class Solution {
   
public:
    bool registerMap(const Struct1 &lhs, const Struct2 &rhs)
    {
   
        return myMap.insert(make_pair<Struct1, Struct2>(lhs, rhs)).second; // C++ 98
    }
private:
    map<Struct1, Struct2> myMap;
};

上段代码中对make_pair的两个模板参数进行了显示定义,不依赖编译器进行模板的型别推导。在 C++98 的环境是可以编译通过的,但是在 C++11 或者更高版本缺无法通过编译,编译器会毫不犹豫得警告你不能这样玩:cannot convert 'lhs' (type 'const Struct1') to type 'Struct1&&',具体原因可以通过看 make_pair 的标准库得知。

#if __cplusplus >= 201103L
  template<class _T1, class _T2>
    constexpr pair<_T1, _T2>
    make_pair(_T1&& __x, _T2&& __y)
    {
   
      return pair(std::forward<_T1>(__x), std::forward<_T2>(__y));
    }
#else
  template<class _T1, class _T2>
    inline pair<_T1, _T2>
    make_pair(_T1 __x, _T2 __y) {
    return pair<_T1, _T2>(__x, __y); }
#endif

上述标准库代码实际上进行了删减,但是不影响我们用来理解 C++高版本带来的变化。在 C++11 版本引入右值后,为适应各个型别在标准库中进行了万能引用的改造。在之前的例子中,对 make_pair 指定模板参数后,编译器会将其推导为如下的形式,自然在传入const Struct &时,编译器就会报错。

constexpr pair<Struct1, Struct2> make_pair(Struct1&& __x, Struct2&& __y)
    {
   
      return pair(std::forward<Struct1>(__x), std::forward<Struct2>(__y));
    }

右值和移动究竟解决了什么问题?

值类别

C++标准里面规定了下面这些值类别:

Modern C++ 学习笔记 —— 右值、移动篇_第1张图片

  • 左值 lvalue是有标识符、可以取地址的表达式,通常有:变量\函数\数据成员的名字,返回左值引用的表达式(++x, x=1),字符串字面量(“hello world”).字符串字面量之所以是左值,是因为它需要占用主存,是可以取地址的。而整数,字符等可以直接放在寄存器,不能取地址。cout << "hello world"s.size() << endl;
  • 右值 rvalue表达式结束后不在存在的历史对象。进一步划分为纯右值和将亡值。
  • 纯右值 prvalue是没有标识符、不可取地址的表达式,一般称为“临时对象”,通常有:返回非引用类型的表达式(x++, x+1),除字符串字面量之外的字面量(42、true).
  • 将亡值 xvalue值即将被销毁,却能够被移动的值,他有地址但是仅编译器能够操作,程序不可访问,资源可以服用。
int a = 3;
int setValue()
{
   
    a = 4; // 4 是字面量,是纯右值
    return a; // 执行结束后 a 成为将亡值
}
int main(void)
{
   
    int b = setValue(); // 右值赋值给左值
    int *p = &setValue(); // 纯右值没有地址
    1 + 4 = 5; // 右值无法在等号左边
    (b > 

你可能感兴趣的:(学习笔记,c++,面试,后端,经验分享,编程语言)