You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何std::forward传入左值却返回右值?完美转发疑问解析

关于std::forward的完美转发疑惑与解析

问题重现

有观点认为std::forward()实现完美转发,能将左值转发为左值、右值转发为右值,但以下代码运行结果看似与此相悖:

#include <iostream>

class A {
 public:
    int x;

    A() {x = 0;}
    explicit A(int x) : x(x) {}

    A& operator= (A&& other) {
        std::cout << " && ="  << std::endl;
        x = other.x;
    }

    A& operator= (const A& other) {
        std::cout << " & ="  << std::endl;
        x = other.x;
    }
};

int main() {
    A p1(1), p2, p3;
    p2 = p1;
    p3 = std::forward<A>(p1);
    return 0;
}

输出结果:

& =
 && =

可见std::forward<A>(p1)将左值p1转发成了右值,结合std::forward的源码及GDB调试信息,对此产生困惑。

std::forward<A>(p1)的调用过程解析

先看std::forward的两个核心重载:

/**
 *  @brief  Forward an lvalue.
 *  @return The parameter cast to the specified type.
 *
 *  This function is used to implement "perfect forwarding".
 */
template<typename _Tp>
constexpr _Tp&&
forward(typename std::remove_reference<_Tp>::type& __t) noexcept
{ return static_cast<_Tp&&>(__t); }

/**
 *  @brief  Forward an rvalue.
 *  @return The parameter cast to the specified type.
 *
 *  This function is used to implement "perfect forwarding".
 */
template<typename _Tp>
constexpr _Tp&&
forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
{
  static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument"
        " substituting _Tp is an lvalue reference type");
  return static_cast<_Tp&&>(__t);
}

当调用std::forward<A>(p1)时:

  1. 模板参数_Tp被显式指定为A,std::remove_reference<A>::type的结果是A,因此匹配第一个重载(参数为A&),传入的p1是左值,符合参数要求。
  2. 函数返回static_cast<A&&>(__t),这里__t是左值引用A&,通过强制转换为右值引用,最终表达式属于将亡值(xvalue),会被识别为右值,因此触发了A的移动赋值运算符(operator=(A&&))。

这并不是std::forward的标准完美转发用法——完美转发的核心是保留原始参数的值类别,需要结合模板参数推导实现:当模板参数T被推导为左值引用时,std::forward<T>返回左值;当T被推导为非引用类型时,返回右值。直接显式指定_Tp为非引用类型(如A),本质是强制将左值转为右值,属于特殊使用方式,而非完美转发的典型场景。

std::forward与std::move的区别

  1. 核心功能不同

    • std::move是无条件将参数转为右值引用,目的是转移对象资源所有权,无论传入左值还是右值,最终都会得到右值引用。
    • std::forward是有条件转发:若模板参数被推导为左值引用(对应原始参数是左值),返回左值;若模板参数是/被指定为非引用类型(对应原始参数是右值),返回右值,以此保留原始值类别。
  2. 实现逻辑不同

    • std::move的简化实现:
      template <typename T>
      constexpr typename std::remove_reference<T>::type&& move(T&& t) noexcept {
          return static_cast<typename std::remove_reference<T>::type&&>(t);
      }
      
      它始终将参数转换为T的右值引用,不依赖条件性的引用折叠。
    • std::forward通过_Tp&&结合引用折叠规则实现条件转发:当_Tp是左值引用T&时,_Tp&&折叠为T&(返回左值);当_Tp是非引用类型T时,_Tp&&就是T&&(返回右值)。
  3. 使用场景不同

    • std::move用于明确需要转移资源的场景,比如将左值对象传递给移动构造/赋值函数,或存入容器时避免拷贝:std::vector<int> v; v.push_back(std::move(temp));。
    • std::forward主要用于模板函数中转发参数,确保参数原始值类别被保留,比如包装函数中传递参数给其他函数:
      template <typename Func, typename... Args>
      auto wrap(Func&& func, Args&&... args) {
          return std::forward<Func>(func)(std::forward<Args>(args)...);
      }
      

内容的提问来源于stack exchange,提问作者uniea

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 06:06:00