【C++】引用、引用初始化、引用折叠规则
引用
引用就好像存储数据的一块内存区域(变量)的一个名字,定义引用就好像声明了一个变量名并把它绑定到已存在的变量上,变量名附带属性(包括但不限于类型、存储期),变量名附带的属性由声明变量名时使用的声明指定符决定。需要注意的是,用于声明左值引用的 & 和用于声明右值引用的 && 不属于声明指定符,它们是声明符的一部分,它们分别决定了引用的左值 / 右值属性 —— 左值引用和右值引用。
通过变量名使用(读写)内存区域(变量),变量名所附带的属性会附加到变量上(除左值引用 / 右值引用属性外),请看代码:
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&&
-
template<typename T>
-
void f(T&& param)
-
{
-
std::cout<<param;
-
}
-
-
f(10); // 10是右值 int &&
-
int x = 10;
-
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