首页 > 编程语言 >【C++】引用、引用初始化、引用折叠规则

【C++】引用、引用初始化、引用折叠规则

时间:2023-04-27 15:33:34浏览次数:37  
标签:初始化 右值 E5% 左值 C++ int 引用 E7%

 

引用
引用就好像存储数据的一块内存区域(变量)的一个名字,定义引用就好像声明了一个变量名并把它绑定到已存在的变量上,变量名附带属性(包括但不限于类型、存储期),变量名附带的属性由声明变量名时使用的声明指定符决定。需要注意的是,用于声明左值引用的 & 和用于声明右值引用的 && 不属于声明指定符,它们是声明符的一部分,它们分别决定了引用的左值 / 右值属性 —— 左值引用和右值引用。

通过变量名使用(读写)内存区域(变量),变量名所附带的属性会附加到变量上(除左值引用 / 右值引用属性外),请看代码:

int a = 10; // 定义一个变量,并声明一个变量名a,变量名a附带的属性是int
const int &refa = a; // 声明变量a的一个名字(引用)refa,变量名refa附带的属性是const int
refa = 15; // 非法,通过refa使用变量,变量的属性是const int,因此不能进行赋值
1
2
3
一个变量,如果没有变量名(包括引用)绑定到这个变量上,那么这个变量是右值,如果为之绑定了变量名(包括引用),那么这个变量就成为左值。变量的左值 / 右值属性不受变量名附带的左值引用 / 右值引用属性影响,仅仅与内存区域绑定。

引用的初始化
引用必须初始化,引用初始化就是将引用绑定到一个变量上。
变量的左值 / 右值属性是内存区域的固有属性,与变量名无关。
引用初始化时,所绑定的变量属性应该和要初始化的引用属性严格匹配,特别地,左值引用只能绑定到左值变量上,右值引用只能绑定到右值变量上。(除了第4点、第5点和第6点中提到的例外情况)
就const属性而言,附带const属性的引用可以绑定到附带非const属性的变量。
就左值 / 右值属性而言,const左值引用可以绑定到右值变量。
就类属性而言,父类引用可以绑定到子类实例。(多态)
当提到引用时…
当定义了一个引用后,提到这个引用表示的是这个引用所绑定的变量。(唯一的例外是关于decltype类型指示符的)请看代码:

int &&a = 10; /*定义了一个int类型的右值变量,变量的值是10,然后声明了一个
右值引用a并将a绑定到这个变量上,于是这个变量成为左值变量*/

int &b = a; /*这里的a表示的是a所绑定的变量,这个变量的属性是int、左值变
量,因此可以将左值引用b绑定到这个变量上*/
1
2
3
4
5
函数的形参是引用
如果函数的形参是引用,那么函数形参的初始化就是调用函数的时候将其绑定到函数实参所表示的变量上,这个过程仍然遵循引用初始化的规则。这看起来有点类似于指针“传地址而不拷贝值”的意味。下面让我们来看一段代码:

void testRight(int &&ref)
{}
void testLeft(int &ref)
{}

int &&ref15 = 15; //合法
testRight(15); //合法,函数的实参表示一个值为15的右值变量
testRight(ref15); //非法,试图将函数形参绑定到左值变量
testLeft(ref15); //合法,函数实参表示一个值为15的左值变量
1
2
3
4
5
6
7
8
9
函数返回引用
类似于引用作为函数形参,当函数返回引用时,函数返回值的初始化就是用 return 语句后表示的变量来初始化返回值,这个过程仍然遵守引用初始化的规则。这看起来有点类似于指针“传地址而不拷贝值”的意味。但多数情况下,这种做法会将返回的引用绑定到即将销毁的函数局部变量上,这是不可取的。

引用折叠规则
引用折叠规则用于类型推导。

引用折叠规则:

​ X& & <=> X&, 左值引用的左值引用等价于左值引用

​ X& && <=> X&, 左值引用的右值引用等价于左值引用

​ X&& & <=> X&, 右值引用的左值引用等价于左值引用

​ X&& && <=> X&&,右值引用的右值引用等价于右值引用

在C++中,直接声明引用的引用是不合法的,但允许 “间接” 声明引用的引用却是被允许的。尽管如此,“间接” 声明的引用的引用还是要被转换成单一引用。转换的规则是引用折叠规则。引用折叠规则会在以下四种上下文中用到:

模板参数的实例化,这种情况下引用折叠规则被用来推断出一个合法的类型参数。请看代码:

template <typename T> void test(T &&val)
{}

int i = 0; const int ci = i;
test(i); // T被推断为 int&,val的类型是 int& &&,折叠为 int&
test(ci); // T被推断为 const int&,val的类型是 const int& &&,折叠为 const int&
test(i * ci); // T被推断为 int,val的类型是 int&&

 

 

 

  1. template<typename T>
  2.   void f(T&& param)
  3.   {
  4.   std::cout<<param;
  5.   }
  6.    
  7.   f(10); // 10是右值 int &&
  8.   int x = 10;
  9.   f(x); // x是左值 int &



typedef类型别名,这种情况下引用折叠规则被用来处理 “间接” 声明的引用的的引用。请看代码:

typedef int &&INTR; // int类型的右值引用类型
INTR &&intrr = 5; // 右值引用的右值引用,折叠为右值引用
1
2
auto类型说明符,这种情况下与第一种情况类似,引用折叠规则被用来推断出一个合法的类型。请看代码:

int a = 10;
auto &&b = a; // auto类型被推断为 int&,b的类型为 int& &&,折叠为 int&
auto &c = 8; // 错误,即使利用引用折叠规则也无法推断出合法的类型
1
2
3
decltype类型指示符,这种情况下与第二种情况类似,引用折叠规则被用来处理 “间接” 声明的引用的引用。请看代码:

int a = 5;
decltype((a)) &&b = a; // decltype推断出的类型是 int&,b的类型是 int& &&,折叠为 int&
————————————————
版权声明:本文为CSDN博主「Telephone2019」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_43737206/article/details/103516871

 

 

from:https://blog.csdn.net/kupepoem/article/details/119944958#:~:text=%E7%94%B1%E4%BA%8E%E5%AD%98%E5%9C%A8T%26%26%E8%BF%99%E7%A7%8D%E4%B8%87%E8%83%BD%E5%BC%95%E7%94%A8%E7%B1%BB%E5%9E%8B%EF%BC%8C%E5%BD%93%E5%AE%83%E4%BD%9C%E4%B8%BA%E5%8F%82%E6%95%B0%E6%97%B6%EF%BC%8C%E6%9C%89%E5%8F%AF%E8%83%BD%E8%A2%AB%E4%B8%80%E4%B8%AA%E5%B7%A6%E5%80%BC%E5%BC%95%E7%94%A8%E6%88%96%E5%8F%B3%E5%80%BC%E5%BC%95%E7%94%A8%E7%9A%84%E5%8F%82%E6%95%B0%E5%88%9D%E5%A7%8B%E5%8C%96%EF%BC%8C%E8%BF%99%E6%98%AF%E7%BB%8F%E8%BF%87%E7%B1%BB%E5%9E%8B%E6%8E%A8%E5%AF%BC%E7%9A%84T%26%26%E7%B1%BB%E5%9E%8B%EF%BC%8C%E7%9B%B8%E6%AF%94%E5%8F%B3%E5%80%BC%E5%BC%95%E7%94%A8,%28%26%26%29%E4%BC%9A%E5%8F%91%E7%94%9F%E7%B1%BB%E5%9E%8B%E7%9A%84%E5%8F%98%E5%8C%96%EF%BC%8C%E8%BF%99%E7%A7%8D%E5%8F%98%E5%8C%96%E5%B0%B1%E7%A7%B0%E4%B8%BA%E5%BC%95%E7%94%A8%E6%8A%98%E5%8F%A0%E3%80%82%201.%E6%89%80%E6%9C%89%E5%8F%B3%E5%80%BC%E5%BC%95%E7%94%A8%E6%8A%98%E5%8F%A0%E5%88%B0%E5%8F%B3%E5%80%BC%E5%BC%95%E7%94%A8%E4%B8%8A%E4%BB%8D%E7%84%B6%E6%98%AF%E4%B8%80%E4%B8%AA%E5%8F%B3%E5%80%BC%E5%BC%95%E7%94%A8%E3%80%82

标签:初始化,右值,E5%,左值,C++,int,引用,E7%
From: https://www.cnblogs.com/im18620660608/p/17359051.html

相关文章

  • 开心档之C++ 预处理器
    C++预处理器预处理器是一些指令,指示编译器在实际编译之前所需完成的预处理。所有的预处理器指令都是以井号(#)开头,只有空格字符可以出现在预处理指令之前。预处理指令不是C++语句,所以它们不会以分号(;)结尾。我们已经看到,之前所有的实例中都有 #include 指令。这个宏用于把头......
  • 开心档之C++ 多态
    C++多态多态按字面的意思就是多种形态。当类之间存在层次结构,并且类之间是通过继承关联时,就会用到多态。C++多态意味着调用成员函数时,会根据调用函数的对象的类型来执行不同的函数。下面的实例中,基类Shape被派生为两个类,如下所示:实例#include<iostream>usingnamespac......
  • 开心档之C++ 引用
    C++引用引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。C++引用vs指针引用很容易与指针混淆,它们之间有三个主要的不同:不存在空引用。引用必须连接到一块合法的内存。一旦引用被初始......
  • 开心档之C++ STL 教程
    C++STL教程在前面的章节中,我们已经学习了C++模板的概念。C++STL(标准模板库)是一套功能强大的C++模板类,提供了通用的模板类和函数,这些模板类和函数可以实现多种流行和常用的算法和数据结构,如向量、链表、队列、栈。C++标准模板库的核心包括以下三个组件:组件描述容......
  • 静态初始化器知多少
    提问静态初始化器有什么特点回答只执行一次,在首次调用时执行2.可以用来初始化变量,并封装异常回答......
  • 记录一次未初始化漏洞_four
    对一道关于未初始化漏洞的题目的总结,来自前几天的DASCTF。这道题总体不算难,我觉得更多的考了代码审计能力(也有可能是本人初学,看伪c没经验,所以觉得很复杂,中间看了看wp对这道题才恍然大悟)因为作为一道栈题来说,伪c算挺长的了。题目链接:https://pan.baidu.com/s/1oLz7BPI5oyJlrO2a5......
  • c++打卡第十七天
    一、问题描述二、设计思路①、对于每个小孩得所得到的糖果数我们可以定义一个数组存储它们。②、需要进行循环结构,同时循环停止的条件为10个小孩得糖果数相同。③、小孩所得得糖果为奇数时需要向老师要一块后才再次分一半给后一个小朋友,这时候我们需要选择结构来实现。④、我们......
  • C++ linux epoll并发服务器模型初探
    socket通讯流程图最简单的可以通讯的C++服务器端代码:#include<stdio.h>#include<unistd.h>#include<sys/types.h>#include<sys/socket.h>#include<arpa/inet.h>#include<netinet/in.h>#defineSERV_PORT8000intmain(void){intlfd......
  • 用C++编写一个简单的发布者和订阅者
    摘要:节点(Node)是通过ROS图进行通信的可执行进程。本文分享自华为云社区《编写一个简单的发布者和订阅者》,作者:MAVER1CK。@[toc]参考官方文档:Writingasimplepublisherandsubscriber(C++)背景节点(Node)是通过ROS图进行通信的可执行进程。在本教程中,节点将通过话题(To......
  • 基于C++的AI贪吃蛇
    访问【WRITE-BUG数字空间】_[内附完整源码和文档]用C++做了个有AI功能的贪吃蛇小游戏,希望大家enjoyit.总体概况开发环境:VIsualStudio2017开发语言:C++和少许WindowsAPI运行环境:Windows1001初始化工作-游戏设置游戏设置和相关初始化放在了一个类里面,并进行了静态声明。主要设......