前言
关于 std::forward
的用法在之前的文章 《C++11中std::move和std::forward到底干了啥》已经总结过了,它被称为完美转发函数,用于函数模板中完成参数转发任务,当形参为左值引用时把它转发成左值,而在形参成为右值引用时把它转发成右值,依靠了引用折叠规则和 std::remove_reference
模板。
前段时间看到std::forward
的源代码时突然有发现有些疑问,后来弄明白了决定换个花样试一试,不过在“恶搞”这个函数之前,先来看一看使用模板的规则,我们以模板函数为例,看看模板是怎么用的。
函数模板
1 | template<class T> |
这是一个非常简单的模板函数,直接传入参数就可以调用这个函数做加法运算,就像下面这样:
1 |
|
函数的运行结果如下:
2022
5.1
happyholiday
我们在调用模板函数时虽然没有指定模板 T
的类型,但是编译器会自动推导,分别生成以下三个函数:
1 | int Add(int a, int b) |
但是当我们采用以下的方式调用函数的时候就会出现编译错误
1 | std::cout << Add(3, 2.1) << std::endl; |
1 | albert@home-pc:/mnt/d/data/cpp/testtemplate$ g++ testt.cpp --std=c++11 |
编译器给出的错误很明显,那就是没有匹配 Add(int, double)
的函数生成,这个模板只提供了一个类型参数,遇到这种情况应该怎么办呢?我们知道 int 可以隐式转换成 double 类型,那就让它默认生成一个类型为 double 的模板函数 Add(int, double)
就可以了,所以这种情况下把调用函数写成下面这样就可以成功编译了:
1 | std::cout << Add<double>(3, 2.1) << std::endl; |
通过这个例子我们发现有些情况下,这个模板函数的参数类型必须显式传递,接下来我们再来熟悉一下 std::forward
函数。
forwawrd 函数定义
先来复习一下函数的定义:
1 | /** |
以上两个模板函数就是用来实现完美转发左值引用和右值引用的,那么你可以试试,当调用第一个函数的时候能不能推导出 _Tp
是什么类型,我之前的疑惑也在这里,这个函数的参数 typename std::remove_reference<_Tp>::type& __t
和模板参数类型 _Tp
看起来关系很密切,但好像又没关系,因为虽然知道参数类型 typename std::remove_reference<_Tp>::type& __t
是个左值引用,但是你并不知道 _Tp
是什么类型,它还是需要显式来指定的,我们接下来试一试和我们想的一不一样。
forwawrd 完美转发
直接拿一个之前写过的完美转发例子吧,代码如下:
1 |
|
编译运行之后的结果如下:
1 | albert@home-pc:/mnt/d/data/cpp/testtemplate$ g++ testf.cpp --std=c++11 |
通过结果我们发现 std::forward
函数在拥有万能引用参数的模板函数中实现了完美转发,左值转发后调用了参数为左值引用的函数,右值转发后调用了参数为右值引用的函数,这时如果我们把调用 std::forward
的地方改一下,去掉指定的参数类型 T
,写成 return Print(std::forward(t));
,然后编译看看会发生什么
1 | albert@home-pc:/mnt/d/data/cpp/testtemplate$ g++ testf.cpp --std=c++11 |
这次出了一个很长的编译错误,看来还是需要指定类型的,既然是需要指定的,那我们指定成其他的有没有问题呢?比如写成下面这样:
1 | return Print(std::forward<float>(t)); |
编译运行结果如下,都变成了右值引用:
1 | albert@home-pc:/mnt/d/data/cpp/testtemplate$ g++ testf.cpp --std=c++11 |
再改成下面这样:
1 | return Print(std::forward<T&>(t)); |
编译运行结果如下,都变成了左值引用:
1 | albert@home-pc:/mnt/d/data/cpp/testtemplate$ g++ testf.cpp --std=c++11 |
完美转发失效
上面的这两个例子能说明完美转发失效了吗?这倒也不能说明,第一个例子全都转发成了右值引用,第二个例子全部转发成了左值引用,和我们指定的类型是一致的,也算实现了完美转发,只不过通过这些例子更加深入的理解了完美转发的含义,就是能保证转化成指定的类型,如果指定的类型是个万能引用,就会根据原始类型来完成转发,本次探索之旅到此也就结束了,解答疑惑是个有趣的事情。
总结
std::forward
的本质还是进行强制类型转换,它会把传入的参数转发成指定的类型- 完美转发其实是可以脱离左值右值概念的,这也是对完美转发更加深入的理解吧
每一点付出终有回报,每一滴汗水从不会白流,可能你看不见也摸不着,但其实它已经悄然声息的改变了你,改变了你周围的点点滴滴~
2022-5-1 22:58:21