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

时间:2009-07-22 10:09:49

标签: c++ arrays reference

以下代码无法编译。

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 &来模拟引用数组。

14 个答案:

答案 0 :(得分:134)

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

  

不应引用引用,没有引用数组,也没有引用指针。

答案 1 :(得分:56)

引用不是对象。他们没有自己的存储空间,只是引用现有的对象。因此,拥有引用数组是没有意义的。

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

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

答案 2 :(得分:28)

这是一个有趣的讨论。很明显,引用阵列是彻头彻尾的非法,但恕我直言的原因并不是说“它们不是对象”或“它们没有大小”。我指出数组本身并不是C / C ++中的完整对象 - 如果你反对,尝试使用数组作为“类”模板参数实例化一些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; }
};

...现在我有一些看起来很像refs数组的东西:

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

现在,这不是一个真正的数组,它是一个运算符重载;例如,它不会像数组(arr)/ sizeof(arr [0])那样做数组通常做的事情。但它完全符合我想要的一系列引用,完全合法的C ++。 除了(a)设置超过3或4个元素是痛苦的,并且(b)它使用一堆?进行计算:可以使用索引进行计算(不使用正常的C指针计算语义索引) ,但仍然索引)。我希望看到一个非常有限的“引用数组”类型,它实际上可以做到这一点。即一个引用数组不会被视为引用的一般数组,而是一个新的“引用数组”,它有效地映射到类似于上面的内部生成的类(但是你遗憾的是无法制作模板。

这可能会有用,如果你不介意这种讨厌:重新''this'作为一个int *的数组并返回一个引用:(不推荐,但它显示了如何正确'array'可以工作):

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

答案 3 :(得分:26)

评论您的编辑:

更好的解决方案是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;
}

答案 4 :(得分:12)

数组可以隐式转换为指针,pointer-to-reference is illegal in C++

答案 5 :(得分:10)

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

使用boost :: reference_wrapper或boost :: tuple;或只是指针。

答案 6 :(得分:9)

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

在其他任何地方,引用被视为仅仅是事物本身,因此sizeof(*arr)应该只是sizeof(int)。但这会使这个数组上的数组指针算术运算错误(假设引用的宽度不同于整数)。为了消除歧义,它是被禁止的。

答案 7 :(得分:4)

你可以非常接近这个模板结构。 但是,您需要使用指向T的表达式初始化,而不是T;所以,虽然你可以很容易地制作一个'fake_constref_array',你将无法像在OP的例子('8')中那样将它绑定到rvalues;

#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;
}

- &GT; A = 0 B = 7 X = {0,8,0}

答案 8 :(得分:2)

参考对象没有大小。如果您编写sizeof(referenceVariable),它将为您提供referenceVariable引用的对象的大小,而不是引用本身的大小。它没有自己的大小,这就是编译器无法计算数组所需大小的原因。

答案 9 :(得分:2)

当您在数组中存储某些内容时,需要知道其大小(因为数组索引依赖于大小)。根据C ++标准未指定引用是否需要存储,因此索引引用数组是不可能的。

答案 10 :(得分:1)

只是添加到所有对话中。 由于数组需要连续的内存位置来存储项目,因此如果我们创建一个引用数组,那么它不能保证它们将位于连续的内存位置,因此访问将是一个问题因此我们甚至不能将所有数学运算应用于数组。

答案 11 :(得分:0)

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

引用类似于别名。如果你要声明一个引用数组,你基本上会告诉计算机,“分配这个无定形的内存,包括散布在周围的所有这些不同的项目。”

答案 12 :(得分:0)

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

简而言之: 引用可以被认为是没有默认构造函数的结构,因此所有相同的规则都适用。

1)从语义上讲,引用没有默认值。只能通过引用某些内容来创建引用。参考文献没有值来表示缺少参考文献。

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

此规则也适用于没有默认构造函数的结构/类。以下代码示例无法编译:

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

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

答案 13 :(得分:-1)

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

你应该要么使用纯C数组,这些数组不能被引用,因为引用只是C ++的一部分。 您采用C ++方式并使用std::vectorstd::array类用于您的目的。

对于编辑的部分:尽管struct是C中的元素,但您可以定义构造函数和运算符函数,使其成为C ++ class。因此,您的struct无法在纯C中编译!