std::move与std::forward用于模板函数时,模板参数<T && a>中的参数a退化成左值的探讨

关于右值的定义可以看我的这篇文章

std::move与std::forward的一些知识参考文章里的说法:

要认识它的作用,需要知道C++中的几条规则:(这里有篇挺好的文章:http://blog.csdn.net/zwvista/article/details/6848582,但似乎因标准的更新,其中的规则已不完全成立了)
1.引用折叠规则:
X& + & => X&
X&& + & => X&
X& + && => X&
X&& + && => X&&
2.对于模板函数中的形参声明T&&(这里的模板参数T,最终推演的结果可能不是一个纯类型,它可能还会带有引用/常量修饰符,如,T推演为const int时,实际形参为const int &&),会有如下规则:
如果调用函数时的实参为U&(这里的U可能有const/volatile修饰,但没有左/右引用修饰了),那么T推演为U&,显然根据上面的引用折叠规则,U& &&=>U&。
如果调用实参为U&&,虽然将T推导为U&&和U都能满足折叠规则(U&& &&=> U&&且U &&=>U&&),但标准规定,这里选择将T推演为U而非U&&。
总结一下第2条规则:当形参声明为T&&时,对于实参U&,T被推演为U&;当实参是U&&时,T被推演为U。当然,T和U具有相同的const/volatile属性。
3.这点很重要,也是上面zwvista的文章中没有提到的:形参T&& t中的变量t,始终是左值引用,即使调用函数的实参是右值引用也不例外。可以这么理解,本来,左值和右值概念的本质区别就是,左值是用户显示声明或分配内 存的变量,能够直接用变量名访问,而右值主要是临时变量。当一个临时变量传入形参为T&& t的模板函数时,T被推演为U,参数t所引用的临时变量因为开始能够被据名访问了,所以它变成了左值。这也就是std::forward存在的原因!当你 以为实参是右值所以t也应该是右值时,它跟你开了个玩笑,它是左值!如果你要进一步调用的函数会根据左右值引用性来进行不同操作,那么你在将t传给其他函 数时,应该先用std::forward恢复t的本来引用性,恢复的依据是模板参数T的推演结果。虽然t的右值引用行会退化,变成左值引用,但根据实参的 左右引用性不同,T会被分别推演为U&和U,这就是依据!因此传给std::forward的两个参数一个都不能 少:std::forward(t)。

通过上面这段描述,能知道当传入f(T && a)的T是int&&时,a将会是一个左值,但T的类型仍然是int &&,看下面两个例子。

std::move基本等同于一个类型转换:static_cast<T&&>(lvalue)

首先
第一个例子,说明 a将会是一个左值

#include <iostream>
#include <typeinfo>

void printValType(int &&val)
{
    std::cout << "int &&" << std::endl;
}   

template<typename T>
void f(T &&a)
{
    printValType(a);
    // printValType(std::forward<T>(a));
}

int main()
{
    int a = 1;
    f(std::move(a));
    // f(a);
}

编译将报错,报错明确指出,printValType(a); 中的a是左值,如下:

第二个例子,说明 T将仍然是int &&类型

注:关于是std::forward< T >(t)的用法,简单来说返回T的类型并使变量 t 转化为该类型,相当于以下语法:
std::forward< T >(t) == static_cast< T >(t)

详细的内容可以看开头的文章。

#include <iostream>
#include <typeinfo>

void printValType(int &&val)
{
    std::cout << "int &&" << std::endl;
}   

template<typename T>
void f(T &&a)
{
    // printValType(a);
    printValType((T)a);
    printValType((T&&)a);
    printValType(static_cast<T&&>(a));
    printValType(static_cast<T>(a));
    printValType(std::forward<T>(a));
    printValType(std::forward<T&&>(a));
}

int main()
{
    int a = 1;
    f(std::move(a));
    // f(a);
}

结果输出如下:
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值