万能引用基本认识:

万能引用是一种类型,就跟int是一种类型一个道理,再次强调,万能引用是一种类型。

读者都知道,右值引用是用符号&&来表示的。右值引用主要是绑定右值的。如

int &&rv = 100;

现在举一个例子:

void myfunc(int &&tmprv)
{cout << tmpv <<endl;return;
}在main主函数中添加代码:
myfunc(10);
int i = 10;
myfunc(i);    // error 右值引用不能绑定到左值。

从以上代码得到一个结论:右值肯定不能接收左值。编译的时候给出错误的报错信息。

现在,将函数myfunc()改成函数模板:

template<typename T>
void myfunc(T&& tmprv)
{cout << "tmprv" << endl;return ;
}

现在观察编译不在报错,看到的事实有两条

(1)这里的函数模板中的tmprv参数能接收左值,也能接收右值。

(2)tmprv的类型是T&&(这两个地址符是属于tmprv),编译都没报错。

现在再次观察一个事实,发现只有函数模板中发生类型模板参数推断时候,也就是推断这个T到底是什么类型的时候,才出现这种tmprv参数,既能接收右值,又能接收左值。即万能引用。

万能引用又称为未定义引用。

1)必须是函数模板

2)必须是发生模板类型推导并且函数模板形参如 T&&

需要注意的是,万能引用T&&,虽然与右值引用很像,但是万能引用存在的场景要求T是类型模板参数。得到一个结论:T&& 是一个万能引用的类型。

template<typename T>
void func(T&& tmprv) {}       //万能引用template<typename T>
void func(std::vector<T>&& param) {...}   //右值引用

完美转发

完美转发,在转发的过程中,这些参数的某些类型的信息会丢失(如参数的const属性,左值、右值属性),显然这种转发就是不完美的,但是如果这些参数的类型信息可以原封不动从funcMiddle函数转发到funcLast函数,这种转发就叫作完美转发。

void funcLast(int v1,int v2)
{++v2;cout << v1 + v2 <<endl;
}在main函数中添加代码
int i = 50;
funcLast(41,i);    //正常显示结果92

可以注意到,在函数funcLast中修改了V2的值,并不影响调用者main()主函数中i的变量。

现在,再增加一个函数模板,这个函数模板的目的是把收到的函数以及这些参数对应的类型不变地转发给其他函数,这里通过一个接收参数的函数模板,把这些参数信息转发给目标函数,那么这个函数模板中参数所收到的参数类型是应该保持不变的,实参如果是const修饰,如果是左值或右值,这些信息都应该保持不变得转发给目标函数,那么怎样做到这些呢。

先写一个函数的模板

template<typename F,typename T1,typename T2>
void funcMiddle_Temp(F f,T1 t1,T2 t2)
{f(t1,t2);
}
在mian主函数中添加如下代码
int j = 70;
funcMiddle_Temp(funcLast,20,j);  

现在看来,funcMiddle_Temp()工作良好,那么修改下funcLast函数的参数,看看funcMiddle_Temp()是否还能够进行正确的转发吗,把funcLast()函数的第2个参数修改为一个引用:

void funcLast(int v1,int& v2)
{++v2;cout << v1 + v2 <<endl;
}
在主函数main添加如下代码
int i = 50;
funcLast(41,i); //正常显示结果92.但是如果把main()函数代码修改为:
int j = 70;
funcMiddle_Temp(funcLast,20,j);   /*正常显示结果91,但j不是71,是70*/

说明转发出现了问题,问题出在funcMiddle_Temp函数模板上,第三个参数t2,它显然会被推导成一个普通的int类型,而不是一个左值引用类型的int(int &)

所以,可以想一想这个funcMiddle_Temp()函数被实例化后的样子。

void funcMiddle_Temp(void(*f)(int, int &),int t1,int t2) {...}

那么怎样做才合适呢,此时万能引用T&&就出场了。

改造下funcMiddle_Temp()函数模板。

template<typename F,typename T1,typename T2>
void funcMiddle_Temp(F f,T1&& t1, T2&& t2) {....}

但是又有新的问题出现了,现在重写一个新的函数funcLast2()的函数,注意第1个函数的形参类型为int && (右值引用类型)

void funcLast2(int&& v1,int& v2)
{cout << v1 << endl;cout << v2 << endl;
}
在main主函数中添加如下代码
int j =70;
funcLast2(20,j);   //显示20.70 没问题

看起来没什么问题,但是如果通过funcMiddle_Temp()函数模板转发或中转一线看行不行,在主函数main中添加代码:

funcMiddle_Temp(funcLast2,20,j);

此时编译失败报错"无法将参数从"T1"转换为'int &&' ". 这是因为funcMiddle_Temp的第二个参数虽然传的是右值,但进到funcMiddle_Temp函数里后就变成左值了。

那么怎么解决呢,就是用完美转发 std::forward了

template<typename F,typename T1,typename T2>
void funcMiddle_Temp(F f, T1&& t1,T2&& t2)
{f(std::forward<T1>(t1),std::forward<T2>(t2));
}

为了进一步加深对std::forward的理解,下面在进行一个范例演示。

namespace _nmsp2
{void printInfo(int& t){cout << "printInfo()参数类型为左值引用" << endl;}void printInfo(int&& t){cout << "printInfo()参数类型为右值引用" << endl;}void printInfo(const int& t){cout << "printInfo()参数类型为const左值引用" << endl;}template <typename T>void TestF(T&& t) //万能引用{printInfo(std::forward<T>(t));}
}

在main函数添加如下代码

_nmsp2::TestF(1);  //1是右值
int i = 5;
_nmsp2::TestF(i);  //i是左值
_nmsp2::TestF(std::move(i)); //move:将左值转成右值
const int j = 8;
_nmsp2::TestF(j);  //j是const左值
_nmsp2::TestF(int(12)); //int(3)是临时对象所以是右值
int&& tmpvalue = 16;
_nmsp2::TestF(tmpvalue); //tmpvalue是左值

运行程序,结果如下:

printfInfo()参数类型为右值引用
printfInfo()参数类型为左值引用
printfInfo()参数类型为右值引用
printfInfo()参数类型为const左值引用
printfInfo()参数类型为右值引用
printfInfo()参数类型为左值引用

C++ 万能引用 与 完美转发相关推荐

  1. 【C++11】右值引用与移动构造、万能引用与完美转发

    目录 一.右值引用 1.1 左值引用和右值引用 1.2 左值引用与右值引用比较 1.3 右值引用的使用场景和意义 二.移动构造 2.1 移动构造的实现 2.2 移动赋值 2.3 默认成员函数 2.4 ...

  2. C++左值与右值の深思——万能引用与完美转发

    目录 传统艺能

  3. linux 无线网卡驱动桥转发,引用和完美转发

    # 右值引用 移动语意 ~~~ 右值引用解决了左值引用无法传递临时对象和常引用传递的对象只读的问题. 右值引用允许传递一个可变的临时对象引用. 移动构造使用移动而非赋值语义完成构造过程, 主要用于解决 ...

  4. C++右值引用和完美转发

    C++右值引用和完美转发 何为引用 引用必须是左值 右值引用 完美转发 move() 使用move的优点 move 左值测试 move 右值测试 注意 参考链接 看到有些同学,调用函数的时候总喜欢使用 ...

  5. C++11特性《 右值引用-<完美转发>、lambda表达式》

    1.右值引用 1.1移动语义 如果一个类中涉及到资源管理,用户必须显式提供拷贝构造.赋值运算符重载以及析构函数,否则编译器将 会自动生成一个默认的,如果遇到拷贝对象或者对象之间相互赋值,就会出错,比如 ...

  6. C++11右值引用、完美转发foward、可变模板参数实例

    本文转载自https://blog.csdn.net/jirryzhang/article/details/82960080 #include <iostream> using names ...

  7. [c++]-c++中的左值和右值、左值引用和右值引用、万能引用和引用折叠及完美转发

    1.左值和右值 1.1左值和右值定义 在c++中,左值是一个指向内存的东西,换句话来讲,左值有地址,保存在内存中,右值则为不指向任何地方东西,即不在内存中占有确定位置.一般来说,右值是暂时和短暂的,而 ...

  8. 万能引用,引用折叠,右值变左值的情况

    文章目录 万能引用 引用折叠 右值变左值的情况 万能引用 左值引用只能引用左值,右值引用只能引用右值. 但是对于一个函数我们有时候并不清楚传入的参数是左值还是右值,这时候就需要写两个同名的函数重载,而 ...

  9. C++11:forward及完美转发

    简介 一个右值引用参数作为函数的形参,在函数内部再转发该参数的时候它已经变成一个左值了,并不是原来的类型. 比如: template <typename T> void forwardVa ...

最新文章

  1. 退出命令_退出不褪色 离别不离志----石嘴山支队举行2019年度冬季消防员退出命令宣布大会...
  2. ecshop的商品列表输出中多出一条空记录
  3. python学多久可以接单-零基础小白多久能学会python
  4. static_const和reinterpret_cast
  5. CodeForces - 1293D Aroma's Search(暴力)
  6. python爬虫面试题
  7. RedHat Linux 7.3基础环境搭建
  8. php正则表达式以及正则函数详解
  9. c#.net全站防止SQL注入类的代码
  10. cpython安装_Cython安装没有找到Python.h文件?
  11. Swagger怎么下载文件
  12. [Mac]『Grid』(格子) 免费又实用的分屏软件
  13. 网页特殊符号HTML代码大全
  14. 国际化的locale类详解
  15. 超级详细的计数问题的解法
  16. 正则表达式:字符串替换
  17. java线程(Thread)的创建与常用方法
  18. 质量检验GB/T 2828
  19. 双斜齿轮-市场现状及未来发展趋势
  20. WML语言基础(WAP建站)五

热门文章

  1. 陈**普通生物学第3版课后答案
  2. 裸辞4个月后,他的人生都经历了什么
  3. git关联远程仓库的方法
  4. 程序员必备:推荐一个谷歌镜像导航网站
  5. 人工智能和机器学习领域有哪些有趣的开源项目?
  6. 空间滤波(平滑滤波)
  7. SQL创建一模一样的表
  8. 【著名博客搬运翻译】无限过程式生成城市使用波函数坍缩算法
  9. EMUI10android系统下载,系统:花粉俱乐部放出EMUI10/安卓10适配进度,有包括你的机型吗...
  10. 打造属于自己的字体(转)