为什么引用数组是非法的?

不编译以下代码。

int a = 1, b = 2, c = 3;
int& arr[] = {a,b,c,8};

C + + 标准是怎么说的呢?

我知道我可以声明一个包含引用的类,然后创建该类的数组,如下所示。但是我真的想知道为什么上面的代码不能编译。

struct cintref
{
cintref(const int & ref) : ref(ref) {}
operator const int &() { return ref; }
private:
const int & ref;
void operator=(const cintref &);
};


int main()
{
int a=1,b=2,c=3;
//typedef const int &  cintref;
cintref arr[] = {a,b,c,8};
}

可以使用 struct cintref而不是 const int &来模拟引用数组。

141266 次浏览

引用不是对象。它们没有自己的存储空间,只是引用现有的对象。由于这个原因,使用引用数组是没有意义的。

如果您想要一个引用另一个对象的轻量级 对象,那么您可以使用一个指针。只有在为所有 struct实例的所有引用成员提供显式初始化时,才能将具有引用成员的 struct用作数组中的对象。不能默认初始化引用。

编辑: 正如 jia3ep 所指出的,在声明的标准部分中明确禁止引用数组。

数组可隐式转换为指针,在 C + + 中指向引用是非法的

引用对象没有大小。如果编写 sizeof(referenceVariable),它将给出 referenceVariable引用的对象的大小,而不是引用本身的大小。它没有自己的大小,这就是为什么编译器无法计算数组需要多少大小。

回答你关于标准的问题,我可以引用 C + + 标准8.3.2/4:

应该没有引用,没有引用数组,没有指向引用的指针。

这是因为引用不是对象,不占用内存,所以没有地址。您可以将它们看作对象的别名。声明一个 nothing 数组没有多大意义。

考虑一个指针数组。指针实际上是一个地址; 所以当你初始化数组时,你类似地告诉计算机,“分配这块内存来保存这些 X 数字(这是其他项目的地址)。”如果你改变其中一个指针,你只是改变它指向的东西,它仍然是一个数字地址,它本身就在同一个位置。

引用类似于别名。如果要声明一个引用数组,基本上就是告诉计算机,“分配这个由散布在各处的所有这些不同项组成的无定形内存块。”

因为正如许多人在这里所说的,引用不是对象。他们只是化名。有些编译器可能将它们实现为指针,但标准并不强制/指定。而且因为引用不是对象,所以不能指向它们。在数组中存储元素意味着存在某种索引地址(即,指向某个索引处的元素) ; 这就是为什么不能有引用数组,因为不能指向它们。

使用 ost: : reference _ wrapper,或者使用 ost: : tuple; 或者只使用指针。

这是一个有趣的讨论。显然,参考文献的数组是完全非法的,但恕我直言,其原因并不像说“它们不是对象”或“它们没有大小”那么简单。我要指出的是,数组本身并不是 C/C + + 中成熟的对象——如果你反对这一点,试着用一个数组作为“ class”模板参数来实例化一些 stl 模板类,看看会发生什么。您不能返回它们,分配它们,将它们作为参数传递。(数组参数被视为指针)。但是创建数组数组是合法的。 引用确实有一个编译器可以而且必须计算的大小——你不能 sizeof ()一个引用,但是你可以创建一个只包含引用的结构。它的大小将足以包含实现引用的所有指针。如果不初始化所有成员,就不能实例化这样的结构:

struct mys {
int & a;
int & b;
int & c;
};
...
int ivar1, ivar2, arr[200];
mys my_refs = { ivar1, ivar2, arr[12] };


my_refs.a += 3  ;  // add 3 to ivar1

实际上,您可以将这一行添加到结构定义中

struct mys {
...
int & operator[]( int i ) { return i==0?a : i==1? b : c; }
};

现在我有了一些看起来很像参考数组的东西:

int ivar1, ivar2, arr[200];
mys my_refs = { ivar1, ivar2, arr[12] };


my_refs[1] = my_refs[2]  ;  // copy arr[12] to ivar2
&my_refs[0];               // gives &my_refs.a == &ivar1

现在,这不是一个真正的数组,它是一个运算符重载; 它不会做数组通常做的事情,例如 sizeof (arr)/sizeof (arr [0])。但是它确实做了我希望引用数组做的事情,使用完全合法的 C + + 。 除了(a)设置超过3或4个元素是一件痛苦的事情,(b)它使用一堆?: 可以使用索引来完成(不是使用普通的 C 指针计算语义索引,但仍然是索引)。我希望看到一个非常有限的“参考数组”类型,它实际上可以做到这一点。也就是说,一个引用数组不会被看作是一个通用的引用数组,而是一个新的“引用数组”,它有效地映射到一个内部生成的类,类似于上面的那个(但不幸的是,这是 不行用模板生成的)。

如果你不介意这种令人讨厌的方法的话,这个方法可能会有用: 重新把’* this’表示为一个 int * 数组,然后返回一个由 int * 表示的引用: (不推荐,但它展示了正确的‘ array’是如何工作的) :

 int & operator[]( int i ) { return *(reinterpret_cast<int**>(this)[i]); }

您可以相当接近这个模板结构。 然而,您需要使用指向 T 的表达式进行初始化,而不是使用指向 T 的表达式进行初始化; 因此,尽管您可以轻松地创建类似的“ false _ constref _ array”,但是您不能像 OP 示例(’8’)那样将其绑定到 rvalue;

#include <stdio.h>


template<class T, int N>
struct fake_ref_array {
T * ptrs[N];
T & operator [] ( int i ){ return *ptrs[i]; }
};


int A,B,X[3];


void func( int j, int k)
{
fake_ref_array<int,3> refarr = { &A, &B, &X[1] };
refarr[j] = k;  // :-)
// You could probably make the following work using an overload of + that returns
// a proxy that overloads *. Still not a real array though, so it would just be
// stunt programming at that point.
// *(refarr + j) = k
}


int
main()
{
func(1,7);  //B = 7
func(2,8);     // X[1] = 8
printf("A=%d B=%d X = {%d,%d,%d}\n", A,B,X[0],X[1],X[2]);
return 0;
}

—— > A = 0 B = 7 X = {0,8,0}

当你在数组中存储东西时,需要知道它的大小(因为数组索引依赖于它的大小)。根据 C + + 标准,没有指定引用是否需要存储,因此不可能对引用数组进行索引。

编辑评论:

更好的解决方案是 std::reference_wrapper

详情: Http://www.cplusplus.com/reference/functional/reference_wrapper/

例如:

#include <iostream>
#include <functional>
using namespace std;


int main() {
int a=1,b=2,c=3,d=4;
using intlink = std::reference_wrapper<int>;
intlink arr[] = {a,b,c,d};
return 0;
}

实际上,这是 C 和 C + + 语法的混合体。

您应该使用纯 C 数组,它不能是引用,因为引用只是 C + + 的一部分。或者你走 C + + 的路线,使用 std::vector或者 std::array类来达到你的目的。

至于编辑的部分: 尽管 struct是来自 C 的元素,但是您定义了构造函数和运算符函数,这使它成为 C + + class。因此,您的 struct将无法在纯 C 中编译!

只是为了增加谈话内容。 因为数组需要连续的内存位置来存储条目,所以如果我们创建一个引用数组,那么不能保证它们会在连续的内存位置,所以访问将是一个问题,因此我们甚至不能对数组应用所有的数学运算。

给定 int& arr[] = {a,b,c,8};,什么是 sizeof(*arr)

在其他任何地方,引用都被看作是事物本身,因此 sizeof(*arr)应该只是 sizeof(int)。但是这会导致这个数组上的数组指针算法错误(假设引用的宽度不相同,则为 int)。为了消除歧义,这是被禁止的。

我相信答案很简单,它与引用的语义规则以及在 C + + 中如何处理数组有关。

简而言之: 引用可以被看作是没有缺省构造函数的结构,因此所有的规则都适用。

1)语义上,引用没有默认值。引用只能通过引用某些东西来创建。引用没有一个值来表示没有引用。

2)当分配一个 X 大小的数组时,程序创建一个默认初始化对象的集合。由于 reference 没有默认值,因此创建这样的数组在语义上是非法的。

这个规则也适用于没有缺省构造函数的 structs/class:

struct Object
{
Object(int value) { }
};


Object objects[1]; // Error: no appropriate default constructor available