std::forward是如何工作的

前置

首先我们要理解左值和右值的区别
以及如下的引用折叠原则

TRRdes
T&&T&左值引用+左值引用->左值引用
T&&&T&左值引用+右值引用->左值引用
T&&&T&右值引用+左值引用->左值引用
T&&&&T&&右值引用+右值引用->右值引用

场景

一个set方法,这个方法的作用是将传入的vector对象引用v,保存到我们的内部成员变量_v,这里实际上我们只是在做一个引用的传递

void set(const std::vector<T> & v) { _v = v; }

如果传入的对象是一个左值,这样做没什么问题,但是当我们需要赋值的对象是一个右值呢?这里假设赋值的vector是一个方法makeAndFillVector()的结果,即我们做以下赋值操作:

_v = makeAndFillVector();

针对右值,编译器将会移动这个vector而不是拷贝它,但是当你使用一个set()来进行这个操作,由于set接收的是一个左值引用,那么依据引用折叠原则,右值参数的属性将会丢失,这时候就会发生拷贝操作。

set(makeAndFillVector()); // makeAndFillVector的结果将会被拷贝到一个临时变量中

为了避免这个拷贝操作,你需要转发操作,这将会在任何时候都产生最优的代码结果。即当你提供一个左值,你希望你的方法将其作为一个左值执行拷贝,当你提供一个右值,你希望你的方法将其作为一个右值执行转移。

如果你希望实现上述操作,常规做法是重载两个方法分别来处理左值和右值:

set(const std::vector<T> & lv) { _v = v; }
set(std::vector<T> && rv) { _v = std::move(rv); }

这里我们可以通过万能引用来得到一个统一的接口,得到如下的模板方法签名:

template<class T>
void perfectSet(T && t);

下面的方法中,v会当作左值引用传递

std::vector<T> v;
perfectSet(v);

但是当我们传入一个右值,makeAndFillVector产生的匿名Vector会当作右值引用传递

perfectSet(makeAndFillVector());

完美转发

perfect的实现中,如果我们想加载到正确的set,就需要使用到完美转发

template<class T>
void perfectSet(T && t) {
    set(std::forward<T>(t));
}

那么,如果不使用std::forward将会发生什么?

我们比较以下两个方法进行参数转发:

void perfectSet(T && t) {
    set(t);
    set(t); // t 没有变化
}
void perfectSet(T && t) {
    set(std::forward<T>(t));
    set(t); // t 此时已经是空的了
}

如果你没有明确如何转发t,编译器必须保守地假设你可能再次访问 t 并选择 set 的左值引用版本。我们这里可以这么认为,虽然我们使用万能引用使prefectSet既可以传入左值也可以传入右值,但是在函数内部,编译器都是假设我们将会再次访问这个形参,这就造成了在函数内部对形参的转发使用都是以左值的形式进行的。

那么我们如何去获得实参信息呢?
在通过万能引用将参数传递进来时,当传入参数是右值,类型T会被推导成实参类型(非引用),如果传入的值是左值,类型T会被推导成左值引用,也就是说模板T中保存着实参信息。

因此,我们需要通过“完美转发”t,编译器将保留它的右值属性,并且将调用 set() 的右值引用版本。 这个版本移动了 t 的内容,这意味着原来的变成了空的。

完美转发原理

这里我们来看一下std::forward的是如何实现“完美转发”的

template<class T>
T&& forward(typename std::remove_reference<T>::type& t) noexcept {
  return static_cast<T&&>(t);
}

我们可以看到,std::forward接收一个T &类型,返回T &&类型。这里通过std::remove_reference解引用获取实参类型t,返回时依据引用折叠原则,最终返回的实际上就是T的类型,保持了实参原来的属性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值