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

Ale*_*tov 135 c++ arrays reference

以下代码无法编译.

int a = 1, b = 2, c = 3;
int& arr[] = {a,b,c,8};
Run Code Online (Sandbox Code Playgroud)

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};
}
Run Code Online (Sandbox Code Playgroud)

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

Kir*_*sky 138

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

不应引用引用,不引用引用数组,也不引用引用指针.

  • @polyglot`还有什么可说的?`_why_的基本原理?我只关注标准,但只是引用它并假设讨论的结束似乎是一种破坏用户批判性思维的可靠方式 - 以及语言发展的任何潜力,例如超出限制或人为限制.有太多'因为标准说'在这里 - 而且还不够',并且由于以下实际原因,这是非常明智的.我不知道为什么upvotes会如此热切地回答那些只说前者而不试图探索后者的答案. (83认同)
  • 还有什么可说的? (28认同)
  • @AaronMcDaid我_think_真正的原因 - 这个和类似的讨论中显而易见的缺点 - 就是如果有一个引用数组,那么如何在元素的地址和它的指示地址之间消除歧义?立即反对'你不能在数组/容器中添加引用/什么'是你_can_放置一个`struct`,其唯一的成员是一个引用.但是,通过这样做,你现在已经有了一个引用和一个父对象来命名......这意味着你可以明确地说明你想要的地址.似乎显而易见......所以为什么没人说呢? (19认同)
  • 应该有引用数组,原因与我们有指针数组相同,信息相同但处理不同,没有? (9认同)
  • 如果编译器开发人员确实作为扩展来决定允许引用数组,那么它会成功吗?或者是否存在一些真正的问题 - 可能是模糊的代码 - 只会使定义此扩展名太麻烦了? (6认同)
  • 我爱上了 C,现在,我正在学习 C++(只是为了通过课程,我不得不承认)。处理家庭作业,我很高兴最后发现引用很有用 - *哦等等*,没有引用数组!太糟糕了。感谢您的简短回答。 (2认同)
  • @SuperCat 明显的用例是通过引用将一组对象传递给一个函数,而无需为每个对象设置单独的参数。当然,我可以用一个指针数组来做到这一点,但这要求所有 NULL 指针检查丑陋,这些 NULL 指针在创建引用时首先要摆脱。 (2认同)
  • @AaronMcDaid ......并且由于没有明确的方法来获取_element_的地址而不是被引用的对象,您将失去在元素之间执行(一致)地址算术的能力.所以现在,你实际上没有数组.你有一些最初看起来像一个数组的东西但是它无法完成人们对数组所期望的许多事情.所以对我来说,即使我们通过指定'X地址永远胜利'或提供消除歧义的语法来限制它......它将是不可接受的模糊(比C++的其余部分更多!)从语法的角度来看'真实'数组 (2认同)
  • 引用不占用内存是什么意思?如果将引用作为成员放入类中,则会占用空间。 (2认同)

CB *_*ley 61

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

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

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

  • 是的,但这是一个实现细节.C++模型中的_object_是一个类型化的存储区域.引用显然不是对象,并且无法保证它在任何特定上下文中占用存储空间. (25认同)
  • 这样说吧:你*可以*只有16个refs到foo的结构,并且使用它与我想要使用我的refs数组完全相同 - 除了你不能索引到16 foo引用.这是一个语言疣恕我直言. (9认同)
  • 引用在本质上与常量指针相同,因此它们占用一些内存(存储)来指向某些东西. (6认同)
  • 不是必须的.如果编译器是对本地对象的引用,则编译器可能能够避免存储该地址. (6认同)
  • 不必要.结构中的引用通常会占用一些存储空间.本地参考通常不会.无论哪种方式,在严格的标准意义上,引用不是对象(这对我来说是新的)命名引用实际上不是_variables_. (4认同)
  • 我不同意拥有refs数组是没有意义的.它可能会使语言的一般语义复杂化,无法修复.我经常写一些东西,比如thing_t const&which = i?thing2:thing1; 在一个循环中,我是0,1,以便在循环中处理thing1和thing2.声明一个本地的ref数组会干净地扩展它以引用很多东西.如果引用是"没有存储的抽象实体",那么你可以有一个抽象数组,它们只能与[]一起使用.这就是问题,你无法获得通用数组语义. (3认同)
  • 顺便说一句,STL(主要是以值语义为导向)确实提供了`std :: reference_wrapper <>`作为一种专门用于引用语义的方法(比如使用[智能]指针的替代方法). (3认同)

gre*_*ggo 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
Run Code Online (Sandbox Code Playgroud)

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

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

...现在我有一些看起来很像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
Run Code Online (Sandbox Code Playgroud)

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

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

 int & operator[]( int i ) { return *(reinterpret_cast<int**>(this)[i]); }
Run Code Online (Sandbox Code Playgroud)


You*_*ouw 27

评论您的编辑:

更好的解决方案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;
}
Run Code Online (Sandbox Code Playgroud)

  • 帖子很旧但不是每个人都知道有关reference_wrapper的解决方案.人们需要阅读有关C++ 11的STL和STDlib的内容. (9认同)

EFr*_*aim 12

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

  • 确实你不能有一个指向引用的指针,但这不是你不能拥有引用数组的原因.相反,它们都是引用不是对象这一事实的症状. (10认同)
  • 结构只能包含引用,并且其大小与引用的数量成正比。如果您确实有一个 refs 'arr' 数组,则正常的数组到指针转换将没有意义,因为 'pointer-to-reference' 没有意义。*但是* 只使用 arr[i] 是有意义的,当 'st' 是一个包含 ref 的结构时,就像 st.ref 一样。唔。但是 &amp;arr[0] 会给出第一个引用对象的地址,而 &amp;arr[1]- &amp;arr[0] 不会与 &amp;arr[2]-&amp;arr[1] 相同 - 它会产生很多奇怪的东西。 (2认同)

nav*_*tor 10

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

请改用boost :: reference_wrapper或boost :: tuple; 或只是指针.


Pau*_*Cbr 9

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

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

  • @DavidSchwartz创建一个`struct`,其中唯一的成员是那个引用并找出..? (4认同)
  • 这应该是公认的答案,而不是简单地在OP上抛出标准的愚蠢的迂腐. (2认同)

Kri*_* A. 6

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

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

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

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

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

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

Object objects[1]; // Error: no appropriate default constructor available
Run Code Online (Sandbox Code Playgroud)

  • 您可以创建一个“Object”数组,只需确保将它们全部初始化:“Object objects[1] = {Object(42)};”。同时,即使初始化了所有引用,也无法创建引用数组。 (3认同)