posts

vnn01

blog_why_std_forward

右值引用在用于表达式时是左值

  • 初接触右值,面对下面代码可能会困惑

    • 输入右值实参123, foo_no_move(int&& x)最终调用的却是bar(int &x),不是bar(int &&x)
      foo_no_move(123); // ❌ 传入右值 → 输出"左值: 123"
    
      void foo_no_move(int &&x) { bar(x); } // ❌ x 是左值,调了 bar(int&)!
    
      void bar(int &x) { std::cout << "左值: " << x << "\n"; }
      void bar(int &&x) { std::cout << "右值: " << x << "\n"; }
    
  • 为什么?

    • 因为调bar(x)时,不是按照x声明类型来选择重载版本的
      • 在bar(x)这个表达式里面,x变量是左值、且丢失引用特性,类型是int
        • 变量声明表示这个变量可以被移动(can),默认使用时是取值,而不是移动该变量
        • std::move重新声明为可移动的,预示即将被移动(will),其实际的移动操作发生在移动构造、移动赋值
    • 为什么这样设计?
      • 因为c++里面命名变量都是左值
      • 因为在函数内部可能需要取右值引用的值,所以默认操作不能移动其状态,只能由程序员负责显示调用std::move再次将其转化为右值,再传递进资源转移的函数
    • 参考:

如何在多层函数传递参数的右值引用

  • 使用std::move转发参数的左值或右值属性到内层函数时,每增加一个需要转发值类别的参数,外层需要重载的函数就增加2倍,是不可维护的
        void foo(int &x) { bar(x); }
        void foo(int&& x) { bar(std::move(x)); }
        void bar(int &x) { std::cout << "左值: " << x << "\n"; }
        void bar(int &&x) { std::cout << "右值: " << x << "\n"; }
    
  • 有没有其他办法解决?
    • 答案是用std::forward<T>()转发参数值类别(左值或右值属性),变长函数模板接收多参数