首页 > 编程语言 >C++完美转发为什么必须要有std::forward?

C++完美转发为什么必须要有std::forward?

时间:2023-09-14 23:35:31浏览次数:44  
标签:std 右值 int 左值 C++ && forward

先看一种情况,它的输出结果是什么?

#include <iostream>
using namespace std;

void F(const int &a)
{
    cout << "int: " << a << endl;
}

void F(int &&a)
{
    cout << "int &&: " << a << endl;
}

template <typename T>
void G(T &&t)
{
    return F(t); // 1  direct call
    // return F(std::forward<T>(t)); // 2  forward call
}

int main()
{
    int i = 1;
    G(i);
    G(2);

    return 0;
}

Output:

int: 1
int: 2

这就不符合我们的预期呀,我们希望G(i)调用左值函数,G(2)调用右值函数,而实际上两者都调用的左值函数。因此,需要std::forward

如果我们采用第2种,输出是什么呢?

return F(std::forward<T>(t)); // 2  forward call

Output:

int: 1
int &&: 2

OK, nice

为什么会这样呢,根本原因在于右值引用指向右值,但本身是左值,也就是说T&& t中,t指向右值,但t本身是左值。
所以,如果不采用发转,不管传进来的是右值,经过右值引用也会变成左值,从而去调用左值函数

用forward(t)当实参,T推断成int&&,所以返回值是int&& &&,折叠成int&&,可以被绑定到F参数的右值引用上,就没有错误了

所以,universal reference 转发时并不完美,只完美了一半,当转发目标的的参数是右值引用时,会出现问题。
std::forward 解决当转发目标的的参数是右值引用时的问题。可以保持原始参数的类型,将实参从原来的类型为右值引用的左值,变成了本身就是右值引用

std::forward做了什么,为什么可以保持类型不变?

C++11为什么要引入 右值引用 这个概念? 它与左值引用有什么本质区别?实际上二者在编译后的汇编层面,就是变量的地址,没有任何区别。那么为什么“画蛇添足”增加个 右值引用 概念?因为要在编译时告诉编译器,这里传入的 “变量的地址”,是个普通变量还是濒死的除了此处不再使用的变量。如果是后者,可以用move语义,直接把它的内容、资源给搬走,不用“深拷贝”了。所以题主的问题,对于函数模板 void G(A &&a),它的内部怎么知道形参对应的实参,是个普通变量,还是濒死的临时变量? 答案是,显然、当然没法知道,只能原封不动的完美的转发给函数模板 void G(A &&a)调用的下一层函数。这就是std::forward的用途。如果你用Visual C++,你可以直接搜一下它所带的STL源代码中std::forward的实现。实际上std::forward啥都不做,就是取变量的地址而已。

标签:std,右值,int,左值,C++,&&,forward
From: https://www.cnblogs.com/lfri/p/16588306.html

相关文章

  • C++-类和对象(3)
    今天,继续和大家分享与类和对象相关的知识,本次文章的内容主要分享拷贝构造函数相关的知识。在学习拷贝构造函数之前,我们先对构造函数和析构函数进行一个总结回顾,在接这往下。构造函数和析构函数的总结回顾不论是构造函数还析构函数,我们只需要抓它们的特性,就可以很好的使用它们了。构......
  • C++下标运算符详解
    C++规定,下标运算符[]必须以成员函数的形式进行重载。该重载函数在类中的声明格式如下:返回值类型&operator[](参数);const返回值类型&operator[](参数)const;使用第一种声明方式,[]不仅可以访问元素,还可以修改元素。使用第二种声明方式,[]只能访问而不能修改元素。在实......
  • C++ STL
    Dev-C++可在工具->编译选项->代码生成/优化->代码生成->语言标准中选择“ISOC++11”或“GNUC++11”来支持C++11的新特性(蓝Dev还不支持C++14)不声明下,区间均为左闭右开区间,typename表示一个数据类型而不是C++的关键字。Containter(容器)vectorvector<t......
  • C++字符串
      1,2这个形式的字符串数组,就和普通数组一样,定义后面的大括号,里面装着每个具体的值,然后3,4直接表示出来,然后其实直接3就OK了,4可能是为了方便看。   字符串数组输入部分1.这个。。先把字符串数组定义好,然后使用cin直接输入进去2.如果想要读入包含空格键之类字符串的话......
  • C++11之智能指针(万字长文详解)
    C++11之智能指针为什么需要智能指针#include<iostream>usingnamespacestd;intdiv(){inta,b;cin>>a>>b;if(b==0)throwinvalid_argument("除0错误");returna/b;}voidFunc(){//1、如果p1这......
  • C++基础语言作用
    C++跟C语言是相关联的。页面排序:includeincludeusingnamespacestd;intmain(){...return0;}cin作为输入,类似于C语言的scanf。输入时添加>>a代表输入a值。可以接收一行内多个数据输入,不可以接收多行数据。cout作为输出,类似C语言的printf。cout默认是在一行内输出,如......
  • ros2迁移c++之package.xml、CMakeLists.txt及编译
    1、package.xml<package><!--1.根标签--> <name><!--2.包名--> <version><!--3.版本号--> <description><!--4.包描述--> <maintainer><!--5.维护者--> <......
  • c++并发编程实战-第3章 在线程间共享数据
    线程间共享数据的问题多线程之间共享数据,最大的问题便是数据竞争导致的异常问题。多个线程操作同一块资源,如果不做任何限制,那么一定会发生错误。例如:1intg_nResource=0;2voidthread_entry()3{4for(inti=0;i<10000000;++i)5g_nResource++......
  • c++ 实现 二叉树的 先序遍历,中序遍历 ,后序遍历
    遍历二叉树跟数组不同,树是一种非线性的数据结构,是由n(n>=0)个节点组成的有限集合。如果n==0,树为空树。如果n>0,树有一个特定的节点,叫做根节点(root)。 对于树这种数据结构,使用最频繁的是二叉树。每个节点最多只有2个子节点的树,叫做二叉树。二叉树中,每个节点的子节点作为根的两个子......
  • C++ 实现 快速排序
    #include<iostream>usingnamespacestd;voidquickSort(int(&)[10],int,int);intpartition(int(&)[10],int,int);voidprintArr(constint(&)[10]);voidswap(int(&)[10],int,int);intmain(){ intarr[10]={23,45,18,6,11,19,22,......