现在我想做下面这样的运算:
int i = 2; int* &rp = &i;
结果是VS2013编译器报错,显示:无法从“int *”转换为“int *&”。
然后我定义一个指针p,指向i:int *p = &i;
结果是int * &rp = p;
并不报错。
为什么会这样呢,按理说p和&i都是int*,为什么一个可以,一个不可以呢?答案就是&i是一个表达式的结果,且不是一个左值
,这对引用而言非常重要。
下面我将详细介绍引用(&)的用法,相信看完之后会对你有很大的帮助。
所谓引用,就是给对象起另外一个名字。通常将引用的声明写成&d的形式来定义引用类型,其中d是声明的变量名。如int i = 20;int &d =i;
就是定义i的引用,声明的变量名为d。
int ival = 1024; int &refVal = ival;//refVal和ival绑定,是ival的另一个名字 //int &refVal2;//错误,引用必须被初始化 int &refVal3 = refVal;//可以多重绑定
上述中,ival、refVal和refVal3完全等价。可以用其任意一个给另外的整型变量赋值;给3个中的任意一个赋值,3个值都改变。说白了,这3个对应的是一块内存。
除此之外,定义引用可以在一条语句中:
int i = 1024,i2 = 2048;//i和i2都是int int &r = i, r2 = i2;//r为引用,和i绑定;r2是int型
现在请判断如下定义是否合法:
int &refVal4 = 10; double dval = 3.14; int &refVal5 = dval;
第一个错误,因为引用的初始值必须是一个对象
第二个是正常定义,第三个错误,因为此处引用的类型的初始值必须是int型对象。
好了,到了这里,我们可以总结一下使用引用的一些限制:
引用必须初始化
;所有引用的类型都要和与之绑定的对象一致
;引用只能绑定在对象上,不能与字面值或某个表达式的计算结果绑定在一起
。在上一小结中,我们总结使用应用必须注意的3点。其实,对于第3点,有两个例外:
例外1:const int & r = 56;//正确,初始化常量引用时允许使用任意表达式作为初值,只要表达式的结果能转换成引用的类型
例外2:可以将基类的引用绑定到派生类对象上
。对于例外2,这里暂不做讨论。关键是要理解例外1,这里用到了const,必须要注意“表达式的结果能转换成引用的类型
”要求,如果转换不成引用的类型,是不能使用的。如,
double dval = 3.14; const int &refVal5 = dval;
dval可以转换为int型,所以上述引用合法。
关于const的引用,可以参考我之前的文章详解const引用和常量指针、指向常量的指针,里面有一小节有详细介绍。
直接看下面的例子:
int i = 1; int *p = &i;//正确:&i为int*类型,p也为int *类型 int* &rp = p;//正确:rp与要绑定的p的类型严格匹配 int* &rp2 = &i;//错误:不满足引用限制的第3条。&i不是一个左值
要想让最后一个定义为正确,可以利用第3条限制的例外1。&i是一个非常量地址,它能转换为常量指针,所以如下定义的引用是正确的。
int* const &rp3 = &i;
再看下面的例子:
const int ci = 10; const int *pc = &ci;// 正确:对常量取地址,总是看成底层const,即指向常量的指针 const int* &rpc = pc;//正确,rpc与要绑定的pc的类型严格匹配
要想引用&ci,由于它为表达式的结果,所以需要使用引用第3条限制的例外1。由于&ci本身就为底层的const指针,加上指针要转化为常量指针,所以要像引用ci,必须用指向常量的常量指针:
const int* const &rpc3 = &ci;
使用引用时,其实很简单,把握住引用的几条规则即可
引用必须初始化
;所有引用的类型都要和与之绑定的对象一致
;引用只能绑定在对象上,不能与字面值或某个表达式的计算结果绑定在一起
;对于第3条,若就是像绑定字面值或表达式的计算结果,必须借用const
。对常量对象取地址是一种底层const,即不是对指针(也就是指向)的常量,而是对指针指向内存中的值是常量,一句话就是指向常量的指针
。上面我们详细介绍了auto的使用规则,但它仅仅是使用规则而已,在实际编程中我们在明确变量类型情况下,还是使用明确的类型。Auto真正在编程时的实际应用如下:
内容参考至:https://www.cnblogs.com/QG-whz/p/4951177.html
我们在使用迭代器时常常会这样操作:
list<int> l1; l1.push_back(1); l1.push_back(2); l1.push_back(3); for (list<int>::iterator i = l1.begin(); i!= l1.end(); i++){ cout << i.operator->() << endl; cout << *i << endl; }
这样list<int>::iterator i = l1.begin()
的声明迭代器i看起来繁琐冗长,我们实际可以用auto代替:auto i = l1.begin();
template <typename _Tx,typename _Ty> void Multiply(_Tx x, _Ty y) { auto v = x+y; std::cout << v; }
如上所示:我们获取x+y的值,但是x、y都是模板类型,我们无法知道其类型,这时就可以使用auto。
template <typename _Tx, typename _Ty> auto multiply(_Tx x, _Ty y)->decltype(_Tx*_Ty) { return x*y; }
上面的例子中,返回值依赖于xy的类型,这里我们需要提前查询xy的数据类型,需要用到decltype操作符,它是C++11标准引入的新的运算符,其目的也是解决泛型编程中有些类型由模板参数决定,而难以表示它的问题。
注意:auto在这里的作用也称为返回值占位,它只是为函数返回值占了一个位置
参考:C++ Primer第5版
以上就是auto的详细介绍。如果有疑问,欢迎评论区下方留言;本人水平有限 ,如有错误,也欢迎在评论区下方批评指正。若是喜欢本文,就帮忙点赞吧!