关于引用返回左值和使用decltype返回数组指针的问题?

1.引用返回左值问题

int &get(int *array, int index){return arry[index];}
int main()
{
    int ia[10];
    for (int i = 0; i != 10; ++i)
    {
        get(ia, i) = i;
    }
}

这段代码摘自C++primer 第五版 课后习题 exercise 6.32

书上也明确说

“函数的返回类型决定函数调用是否是左值。调用一个返回引用的函数得到左值,其他返回类型得到右值”

也就是说因为函数类型是引用类型,所以返回的是左值,所以get(ia, i)是一个左值,所以i才能给get(ia, i)赋值,如果函数声明成int get(int *array, int index){return arry[index];}因为函数不是引用类型,返回的是右值,所以不能被赋值,我这样理解没错吧?(可能我描述中有概念弄错,请指出,我感觉我那句“get(ia, i)是一个左值”好像有点概念上描述的错误)
希望大神在给我指导一下!谢谢啦!

2.

int odd[] = { 1, 3, 5, 7, 9 };
int even[] = { 0, 2, 4, 6, 8 };
decltype(odd) *arrPtr(int i)
{
    return (i % 2) ? &odd : &even;
}

以上代码摘自C++primer第五版6.3.3节内容

疑问是为何返回的是&odd(&even)而不是odd(even),数组名不是可以自动变成指向数组首元素的指针吗?
下面说一下我的理解,不知道对错?求指导!
先举个例子:

int a[2] = { 2, 4 };
int *p1 = a;
decltype(a) *p2 = &a;

int *p1 = a; 这里数组名a自动变成指向数组首元素的指针,这里的指针针对的是数组的某个元素,而decltype(a)获得是数组类型,加上*表示指向含有2个整数的数组的指针,这里的指针是指向数组整体的,而非单个元素,所以需要&a,但是&a的结果不也是数组首元素的地址吗?为何非要写成这种形式呢?直接decltype(a) * p2 = a;是错误的。
然后我又测试了一下输出
代码如下:

cout << *p1 << endl;//2
cout << *(*p2 + 1) << endl;//4

发现p2竟然是个指向指针的指针,貌似是证明了我的猜想,p2是指向数组整体的,要想访问数组元素,需要两次解引用,这也是为什么decltype(a) *p2 = &a;而不是decltype(a) * p2 = a;
不知道自己说的对不对,即使对的话,总感觉自己好差点什么东西没理解,希望大神指导一下,给我一种豁然开朗的感觉!

然后我又测试了一下这个问题,代码如下:

cout << p1 << endl;//00488860
cout << *p2 + 1 << endl;//00488864
cout << p2 << endl;//00488860
cout << &a << endl;//00488860
cout << &a + 1 << endl;//00488868

*p2 + 1只是向前移动一位元素,但是为何&a + 1直接跳过整个数组呢?(我还测试数组元素是三个的情况,发现是加12)

以上,写的逻辑很乱,因为很多东西都是我在提问的时候突然想到,然后又在编译器中调试,然后加上来提问的,希望见谅!

阅读 5.2k
2 个回答
  1. 分析的很对,不用抠描述细节,把本质说清楚就行了。
  2. 不得不说,我看了好几遍你的问题。。。(是不是有点将简单问题复杂化了呢。。。)

给出数组:

int a[2] = { 2, 4 };

说来说去其实就仨类型,先给你摆出来:

cppusing type1 = int[2];
using type2 = int(*)[2];
using type3 = int(&)[2];

而decltype(a)获得是数组类型

看来 decltype(a) 的类型不用多说,正如 @Windoze 所回答的,在这里是 type1 类型。

那么你列举的 decltype(a) * p2 = a; 就相当于 type2 p2 = a; 即:

等号左边是 int(*)[2],等号右边是 int[2],这显然不能画等号。


cout << *(*p2 + 1) << endl;//4

再看你列举的这个 *(*p2 + 1) 是什么鬼:

decltype(a) * p2 <==> type1 *p2

所以 *p2 类型就是 type1a 的类型呢?也是 type1,若有 decltype(a) * p2 = &a;*p2 就是 a.

故:

*(*p2 + 1) <==> *(a + 1) <==> a[1] <==> 4

没啥问题吧?


最后说说你那一大堆测试:

cppcout << p1 << endl;//00488860
cout << *p2 + 1 << endl;//00488864
cout << p2 << endl;//00488860
cout << &a << endl;//00488860
cout << &a + 1 << endl;//00488868

画图解释:

 __________ __________ __________ __________
|_____2 ___|____4_____|_____x____|_____x____|
|0x00488860|0x00488864|0x00488868|0x00488872|
|<--------&a--------->|<--------&a+1------->|
|<----p--->|<--p+1--->|

刚才说了,a的类型是 int[2],那么 &a 的类型就是 int(&)[2](也就是我留着没说的 type3)。而 int *p = a;,这个 p 的类型是 int*。而 *p2 + 1 <==> a + 1 <==> p + 1。三种类型如上图所示范围。

骐骥一跃,相当于驽马“两”驾啊。

综上所述,你可能混淆了 type1type2type3 三种类型的区别。区分开,其实非常简单。

7.1.6.2/4
The type denoted by decltype(e) is defined as follows:
— if e is an unparenthesized id-expression or an unparenthesized class member access (5.2.5), decltype(e) is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;
— otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e;
— otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;
— otherwise, decltype(e) is the type of e.

所以decltype(odd)返回的类型是int[5],你所说的自动decay(数组变指针)规则对decltype不生效。
这个类型就是个数组,不是指针,也不是元素指针,它的size是sizeof(int[5])而不是sizeof(int),所以你++一下这个类型的指针就跳过了整个数组。

如果你要指针,你应该手动decay,比如:

    std::decay<decltype(odd)>::type arrPtr(...) {
        ...
    }
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题