首页 > 编程语言 >c++关于 左右值 和 左右值引用 及 函数参数(万能引用,引用折叠,forward完美转发)

c++关于 左右值 和 左右值引用 及 函数参数(万能引用,引用折叠,forward完美转发)

时间:2023-06-06 21:22:38浏览次数:43  
标签:rr 右值 int 左值 c++ 函数参数 引用 &&

左右值和左右值引用是有区别的。

左右值是指对变量类别的区分,左值是有地址的值,可以长期存在;而右值是将亡值,是临时量,没有名字。

而左右值引用是指变量的类型,如int&, int&&等,下面举一个例子:

void func(int &p) {
    cout << "&p" << endl;
    return;
}

void func(int &&p) {
    cout << "&&p" << endl;
    return;
}

int main()
{
    int k = 1;
    int &lr = k;
    int &&rr = move(k);
    func(k);
    func(lr);
    func(rr);
    func(forward<int>(rr));
    func(1);
    
    return 0;
}

vs2022输出如下:

这里k被编译器推导为int &类型。至于rr的输出是&p,需注意区分左右值和左右值引用的关系。

此处rr是一个变量类型为int的右值引用的左值,因此rr作为参数和左值参数匹配。

至于后两个输出具体见下文分析。


 

上例中也可以看出,当函数参数类型为左值引用时,匹配左值参数,右值引用匹配右值参数。

这里可以再提一下引用折叠和万能引用的内容。

补充知识:

万能引用即模板中T&&或auto &&,必须注意当且仅当存在类型推导时&&才会被认为是万能引用。

左值和右值在模板推导时是存在差异的,对于类型T的lvalue(左值),模板会推导为T&类型;但是对于类型T的右值,模板会推导为T。

在模板函数中,模板参数类型由编译器去自动匹配时,左值会匹配为t&,右值会匹配为t&&

using namespace std;

template<typename T>
void print(T& value) {
    cout << "左值" << endl;
}

template<typename T>
void print(T&& value) {
    cout << "右值" << endl;
}

template<typename T>
void func(T&& value) {
    print(value);
    print(forward<T>(value));
}

int main()
{
    int k = 1;
    int &lr = k;
    int &&rr = move(k);
    cout << "k" << endl;
    func(k);
    cout << "lr" << endl;
    func(lr);
    cout << "rr" << endl;
    func(rr);
    cout << "1" << endl;
    func(1);
    return 0;
}

vs2022输出如下:

 这里逐一分析一下。对于前三者k,lr,rr,他们三者均为左值,因此编译器会推导出其类型为int &,从而T的类型为int &。

函数func的参数类型为T &&。&&之所以被称为万能引用,是因为引用折叠的原理。

对于前三者k,lr,rr,T=int &,T &&=int & &&=int &。因此该处函数参数为左值引用,而传入参数又为左值,故匹配,func可被正常调用。

对于常数1,其是右值,T=int。(这里需注意万能引用绑定到右值模板会推导为T而非T&&),因此T&&=int &&,(注意此处未发生引用折叠)。因此参数类型为int&&可正常匹配右值1.

所以说左右值都可以匹配函数func,故而称模板参数T&&为万能引用。

再看程序输出,k,lr,rr均为左值,到函数内部也是左值,因此输出均为左值。

1是右值,匹配到函数内,参数value获取了该右值,但是对于value来说,其是一个类型为右值引用的左值,因此直接将其作为print的参数会输出左值。

但是使用forward函数转发,forward函数会根据参数类型决定返回值类型,若参数为右值或右值引用,则会返回右值,反正返回左值,因此得到的返回值是右值。

 

本文为个人理解,仅供参考,若有错误欢迎指出

标签:rr,右值,int,左值,c++,函数参数,引用,&&
From: https://www.cnblogs.com/Explosion556/p/17461760.html

相关文章

  • c++ 关于函数返回值问题
    c++中,当函数返回基本元素时,一般不会产生异常情况。但是当返回引用或指针时,即不使用值传递而是引用或指针传递来实现,那么需注意:不能返回函数内部的局部变量指针或引用。因为局部变量是在栈上,当离开函数作用域时,其内容会失效,相应的返回的指针或引用指向的内容就没有意义了。不能返......
  • [c++实践]关于标准库中字符串的高效处理
    [c++实践]关于标准库中字符串的高效处理无论什么程序,都需要大量的使用字符串,c++标准库提供了std::string对字符串进行处理。熟悉std::string实现逻辑的都知道,std::string库比较耗时的操作主要在内存的分配与字符串的拼接。因为内存分配实际上还是使用的malloc,但是在多线程......
  • C++ 中的 String 类
     C++标准库提供了 string 类类型,支持上述所有的操作,另外还增加了其他更多的功能。我们将学习C++标准库中的这个类,现在让我们先来看看下面这个实例:现在您可能还无法透彻地理解这个实例,因为到目前为止我们还没有讨论类和对象。所以现在您可以只是粗略地看下这个实例,等理解了......
  • C++中的显式构造函数
      有如下一个简单的复数类:classClxComplex{public:ClxComplex(doubledReal=0.0,doubledImage=0.0){m_dReal=dReal;dImage=dImage;}doubleGetReal()const{returnm_dReal;}doubleGetImage()const{retu......
  • C++类构造函数、拷贝构造函数、复制构造函数、复制构造函数、构造函数显示调用和隐式
    一、构造函数是干什么的 1.2.{3.public:4.//类Counter的构造函数5.//特点:以类名作为函数名,无返回类型6.Counter()7.{8.0;9.}10.private:11.//数据成员12.intm_value;13.}      该类对......
  • C++智能指针的原理和实现
    一、智能指针起因在C++中,动态内存的管理是由程序员自己申请和释放的,用一对运算符完成:new和delete。new:在动态内存中为对象分配一块空间并返回一个指向该对象的指针;delete:指向一个动态独享的指针,销毁对象,并释放与之关联的内存。使用堆内存是非常频繁的操作,容易造成......
  • C++ 中的信号的处理
    C++ 信号处理信号是由操作系统传给进程的中断,会提早终止一个程序。在UNIX、LINUX、MacOSX或Windows系统上,可以通过按Ctrl+C产生中断。有些信号不能被程序捕获,但是下表所列信号可以在程序中捕获,并可以基于信号采取适当的动作。这些信号是定义在C++头文件<csignal>中。......
  • C#调用C++dll
    C#调用C++dll的方法和步骤其他分享涉及到的概念和方法对于像我这样比较菜的选手看起来比较费劲并且很难抓住重点,这里我总结了一段时间的研究成果供初学者救济之用,简单明了。工具/原料 VS2008方法/步骤 新建项目->VisualC++->Win32项目 MyDLL注意:C++编写的dll一般是不能直接拿来......
  • C#中调用c++的dll具体创建与调用步骤,亲测有效~ (待验证)
    使用的工具是VS2010哦~其他工具暂时还没试过我新建的工程名是my21dll,所以会生成2个同名文件。接下来需要改动的只有画横线的部分下面是my21dll.h里面的。。。下面的1是自动生成的不用动,或者也可以不要,因为只是一个宏而已下面可以做相应修改。下面的2是自动生成的类,我没用就注释掉了......
  • C++面试八股文:如何在堆上和栈上分配一块内存?
    某日二师兄参加XXX科技公司的C++工程师开发岗位6面:面试官:如何在堆上申请一块内存?二师兄:常用的方法有malloc,new等。面试官:两者有什么区别?二师兄:malloc是向操作系统申请一块内存,这块内存没有经过初始化,通常需要使用memset手动初始化。而new一般伴随三个动作,向操作系统申请一......