180

The following code does not compile.

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

What does the C++ standard say about this?

I know I could declare a class that contains a reference, then create an array of that class, as shown below. But I really want to know why the code above doesn't compile.

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

It is possible to use struct cintref instead of const int & to simulate an array of references.

Jeffrey Bosboom
  • 13,313
  • 16
  • 79
  • 92
Alexey Malistov
  • 26,407
  • 13
  • 68
  • 88
  • 1
    Even if the array was valid, storing a raw '8' value in it wouldn't work. If you did "intlink value = 8;", it would die horribly, because it's pretty much just translated into "const int & value = 8;". A reference must reference a variable. – Grant Peters Jul 22 '09 at 11:00
  • 3
    `intlink value = 8;` does work. check if you does not believe. – Alexey Malistov Jul 22 '09 at 11:10
  • 7
    As Alexey points out, it is perfectly valid to bind an rvalue to a const reference. – avakar Jul 22 '09 at 11:57
  • 2
    What _doesn't_ work is that `operator=`. References cannot be reseated. If you really want such semantics - though I've not personally found a situation in which they're practically useful - then `std::reference_wrapper` would be the way to do it, as it actually stores a pointer but provides reference-like `operator`s and _does_ allow reseating. But then I'd just use a pointer! – underscore_d Jul 22 '16 at 10:36
  • 1
    The operator= is private and not implemented, aka what in C++11 is =delete. – Jimmy Hartzell Dec 21 '16 at 21:34
  • 1
    @underscore_d - In this context, the "=" is performing initialization, not assignment. So that is not the problem. Not sure I have seen a good rationale yet, actually – Nemo Apr 19 '17 at 18:10
  • @JimmyHartzell Good point, thanks. I'm so used to C++11 now that I often don't recognise the old tricks! – underscore_d Apr 20 '17 at 08:33

14 Answers14

209

Answering to your question about standard I can cite the C++ Standard §8.3.2/4:

There shall be no references to references, no arrays of references, and no pointers to references.

That's because references are not objects and doesn't occupy the memory so doesn't have the address. You can think of them as the aliases to the objects. Declaring an array of nothing has not much sense.

Kirill V. Lyadvinsky
  • 97,037
  • 24
  • 136
  • 212
  • 44
    What more is there to say? – polyglot Jul 22 '09 at 11:13
  • 17
    there should be arrays of references for the same reason we have arrays of pointers, same information but different handling, no? – neu-rah Aug 12 '14 at 03:16
  • 1
    C++ standard says - ** your logic, let ours be! – AnArrayOfFunctions Nov 30 '14 at 19:02
  • 2
    I fell in love with C and right now, I am learning C++ (just to pass the class, I have to admit). Dealing with a home assignment, I was happy that finaly I found references useful - *oh wait*, no arrays of references! Too bad. Thanks for short answer. – David Jan 16 '15 at 21:30
  • 8
    If compiler developers did decide, as an extension, to allow arrays of references, then would it be a success? Or are there some real problems - perhaps ambiguous code - that would just make it too darn confusing to define this extension? – Aaron McDaid Jan 31 '15 at 18:24
  • @David For what purpose would you want an array of references though?... In C++? – Super Cat Sep 17 '15 at 05:19
  • 3
    @SuperCat The obvious use case is to pass a collection of objects into a function by reference, without needing a separate parameter for each one. Sure, I can do that with an array of pointers, but that mandates all the NULL pointer checking ugliness that references were created to get rid of in the first place. – dgnuff Nov 26 '15 at 01:11
  • @dgnuff if the references really belong together, why not have the objects themselves within a collection, and pass a reference to the collection itself? this seems better than either an array of pointers or an array of references (which is not possible) – tstittleburg Apr 22 '16 at 16:27
  • @tstittleburg that is certainly a solution, but has one huge shortcoming. I can't iterate over the members of a collection, either a `struct` or a `class`, using a `for` loop. If the collection has a couple of hundred members, that's going to make processing them all extremely painful, while an array and a `for` loop could do the job in 20 lines or less. – dgnuff Apr 22 '16 at 18:25
  • @dgnuff sorry I wasn't clear. what I meant by collection was an iterable collection like a vector or a list. passing a reference to such a collection would let you do this. – tstittleburg May 03 '16 at 12:19
  • 1
    @tstittleburg OK, but now we're right back to where we started, because your iterable container can't currently contain references -- edit -- std::reference_wrapper<> notwithstanding, but then that solves the original problem, does it not? – dgnuff May 03 '16 at 20:49
  • 33
    @AaronMcDaid I _think_ the real reason - which is so conspicuously absent from this & similar discussions - is that if one had an array of references, how would one possibly disambiguate between the address of an element & the address of its referent? The immediate objection to 'You can't put a reference in an array/container/whatever' is that you _can_ put a `struct` whose sole member is a reference. But then, by doing so, you've now got both a reference & a parent object to name... which now means you can unambiguously state which address you want. Seems obvious... so why has no one said it? – underscore_d Jul 22 '16 at 06:52
  • 5
    @AaronMcDaid ...& by not having a clear way to take the address of an _element_, rather than the referred object, you lose the ability to perform (consistent) address arithmetic between elements. So now, you don't actually have an array. You'd have something that initially looked like an array but that would be unable to do many of the things that people expect from arrays. So to me, even if we limited it by specifying 'X address always wins' or providing disambiguating syntax... it would be unacceptably ambiguous (more so than the rest of C++!) wrt 'real' arrays from a syntactic point of view – underscore_d Jul 22 '16 at 06:57
  • 140
    @polyglot `What more is there to say?` A rationale for _why_? I'm all about the Standard, but just citing it and assuming that's the end of the discussion seems like a sure-fire way to destroy users' critical thought - along with any potential for the language to evolve, e.g. beyond limitations of omission or artificial restriction. There's too much 'because the Standard says' here - and not enough 'and it is very sensible to say that, due to the following practical reasons'. I don't know why upvotes flow so eagerly to answers that only say the former without even trying to explore the latter. – underscore_d Jul 22 '16 at 07:13
  • It's defined in the language to be outside the syntax. Furthermore, a reference is in essence a immutable address/label to a primary value. Take the example `int &i = 1;` Where is the int created? It's not on the stack frame because by declaring i as a reference, `i` itself is allocated no space on the stack. How would it make sense to create a reference to something which has no allocation semantics? – polyglot Jul 24 '16 at 17:06
  • What I'm not clear on, is if I can do const std::string& foo = "some text"; why SHOULDN'T I be ABLE to do const std::string& fooArray[]= {"one", "two", "three"}; – SMGreenfield Aug 27 '19 at 04:09
  • 1
    @polyglot, I logged in just to upvote your comment. I paid for college classes as a young man where the instructor simply said, "because that's the standard," or some such wasteful thing! – Max Nov 10 '20 at 14:59
  • Max: you should check his first comment. And then, check @underscore_d 's comment. – André Caldas May 24 '21 at 16:18
  • 2
    What do you mean references don't occupy memory? If you put a reference as a member inside a class it takes up space. – Zebrafish Aug 30 '21 at 16:47
  • 1
    References seem like a great idea, but when all is said and done they are nothing else than poor man's pointers which can be used with `.` instead of `->`. – Danvil Jan 23 '22 at 19:57
  • The "why" comes down primarily down to how the two are handled internally. Pointer is an actual thing, a variable created in memory, that contains a memory address of something. You can copy, duplicate, arrange, change them like every variable. Reference is just an abstraction that exists in the compiler's "mind". There's no address, no value stored, it's just a nickname for something else. In Java references are more substantial - essentially pointers sugar-coated with an automatic dereferencing interface on top. In C++, they have no such substance. They don't exist in the compiled code. – SF. Feb 22 '23 at 13:10
73

References are not objects. They don't have storage of their own, they just reference existing objects. For this reason it doesn't make sense to have arrays of references.

If you want a light-weight object that references another object then you can use a pointer. You will only be able to use a struct with a reference member as objects in arrays if you provide explicit initialization for all the reference members for all struct instances. References cannot be default initalized.

Edit: As jia3ep notes, in the standard section on declarations there is an explicit prohibition on arrays of references.

CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • 9
    References are same in their nature as constant pointers, and therefore they take up some memory (storage) to point to something. – inazaruk Jul 22 '09 at 10:14
  • 7
    Not necesserily. The compiler might be able to avoid storing the address, if it's a reference to local object for instance. – EFraim Jul 22 '09 at 10:15
  • 5
    Not necessarily. References in structures usually take up some storage. Local references often don't. Either way, in the strict standard sense, references are not objects and (this was new to me) named references aren't actually _variables_. – CB Bailey Jul 22 '09 at 10:17
  • Well, those are compiler-dependent optimizations. In general case references do take some memory. – inazaruk Jul 22 '09 at 10:17
  • 29
    Yes, but this is an implementation detail. An _object_ in the C++ model is a typed region of storage. A reference is explicitly not an object and there is no guarantee that it takes up storage in any particular context. – CB Bailey Jul 22 '09 at 10:19
  • 4
    I don't agree that it doesn't 'make sense' to have arrays of refs. It may be that it would tend to complicate the general semantics of the language beyond repair. I've often written things like thing_t const &which = i? thing2 : thing1; inside a loop where i is 0,1, in order to treat thing1 & thing2 across the loops. Declaring a local array of refs would extend this cleanly to reference many things. If references are 'abstract entities with no storage', then you could have an abstract array of them which can only be used with []. That's the rub, you could not get general array semantics. – greggo Nov 10 '11 at 23:45
  • 12
    Put it this way: you *can* have a struct of nothing but 16 refs to foo's, and use it in exactly the same way as I'd want to use my array of refs - except that you can't index into the 16 foo references. This is a language wart IMHO. – greggo Nov 10 '11 at 23:48
  • 5
    As an aside, the STL (say mostly value semantics oriented) - does provide `std::reference_wrapper<>` as one method specifically for reference semantics (say as an alternative to using [smart] pointers). – wardw Jul 16 '14 at 12:46
39

This is an interesting discussion. Clearly arrays of refs are outright illegal, but IMHO the reason why is not so simple as saying 'they are not objects' or 'they have no size'. I'd point out that arrays themselves are not full-fledged objects in C/C++ - if you object to that, try instantiating some stl template classes using an array as a 'class' template parameter, and see what happens. You can't return them, assign them, pass them as parameters. ( an array param is treated as a pointer). But it is legal to make arrays of arrays. References do have a size that the compiler can and must calculate - you can't sizeof() a reference, but you can make a struct containing nothing but references. It will have a size sufficient to contain all the pointers which implement the references. You can't instantiate such a struct without initializing all the members:

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

In fact you can add this line to the struct definition

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

...and now I have something which looks a LOT like an array of 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

Now, this is not a real array, it's an operator overload; it won't do things that arrays normally do like sizeof(arr)/sizeof(arr[0]), for instance. But it does exactly what I want an array of references to do, with perfectly legal C++. Except (a) it's a pain to set up for more than 3 or 4 elements, and (b) it's doing a calculation using a bunch of ?: which could be done using indexing (not with normal C-pointer-calculation-semantics indexing, but indexing nonetheless). I'd like to see a very limited 'array of reference' type which can actually do this. I.e. an array of references would not be treated as a general array of things which are references, but rather it would be a new 'array-of-reference' thing which effectively maps to an internally generated class similar to the one above (but which you unfortunately can't make with templates).

this would probably work, if you don't mind this kind of nasty: recast '*this' as an array of int *'s and return a reference made from one: (not recommended, but it shows how the proper 'array' would work):

 int & operator[]( int i ) { return *(reinterpret_cast<int**>(this)[i]); }
greggo
  • 3,009
  • 2
  • 23
  • 22
  • 1
    You *can* do this in C++11 `std::tuple abcref = std::tie( a,b,c)` - which creates an "array" of references that can only be indexed by compile-time constants using std::get. But you can't do `std::array abcrefarr = std::tie( a,b,c)`. Maybe there's a way to do it that I don't know of. It seems to me that there should be a way to write a specialization of `std::array` which *can* do this - and thus a function `std::tiearray(...)` which returns one such (or allow `std::array` to accept initializer containing addresses = {&v1, &v2 etc}) – greggo Sep 12 '14 at 22:26
  • 1
    Your proposal is silly (IMO), and your last line assumes an int can hold a pointer (usually false, at least where I work). But this is the only answer here with a legitimate rationale for why arrays of references are forbidden, so +1 – Nemo Apr 19 '17 at 22:46
  • @Nemo it wasn't really intended as a proposal, but just to illustrate that the functionality of such a thing is not absurd on its face. I note in my last comment that c++ has things which come close. Also, the last line you refer to assumes that memory implementing a _reference_ to an int can be usefully aliased as a pointer-to-int -- the struct contains references, not ints -- and that tends to be true. I'm not claiming it's portable (or even safe from 'undefined behaviour' rules). it was meant to illustrate how indexing could be used under-the-hood to avoid all the `?:` – greggo Apr 21 '17 at 17:57
  • Fair enough. But I do think this idea is "absurd on its face", and this is precisely why arrays of references are disallowed. An array is, first and foremost, a container. All containers need an iterator type for standard algorithms to apply. The iterator type for "array of T" is normally "T *". What would be the iterator type for an array of references? The amount of language hacking it would take to deal with all of these issues is ridiculous for an essentially useless feature. Actually maybe I will make this an answer of my own :-) – Nemo Apr 21 '17 at 21:40
  • @Nemo It doesn't need to be part of the core language, so the iterator type being 'custom' is no big deal. All the various container types have their iterator types. In this case the iterator, dereferenced, would reference the target objs, not the refs in the array, which is fully consistent with the array's behaviour. It may still require a bit of new C++ behaviour to support as a template The very simple `std::array`, easily defined in app code, wasn't added to std:: until c++11, presumably because it's warty to have this with no way to do `= {1,2,3};` Was that 'language hacking'? – greggo Apr 24 '17 at 14:31
  • .. and at that point there's no reason the implementation needs to contain references. It can contain an array of pointers, but the `operator []` and iterator would act as if it contained reference (you couldn't change the pointers). The only part missing is the initialization, as far as I can see. Including extending lifetime of any temps. – greggo Apr 24 '17 at 14:38
34

Comment to your edit:

Better solution is std::reference_wrapper.

Details: http://www.cplusplus.com/reference/functional/reference_wrapper/

Example:

#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;
}
Youw
  • 799
  • 6
  • 11
15

Given int& arr[] = {a,b,c,8};, what is sizeof(*arr) ?

Everywhere else, a reference is treated as being simply the thing itself, so sizeof(*arr) should simply be sizeof(int). But this would make array pointer arithmetic on this array wrong (assuming that references are not the same widths as ints). To eliminate the ambiguity, it's forbidden.

Stefan Zobel
  • 3,182
  • 7
  • 28
  • 38
PaulMurrayCbr
  • 1,167
  • 12
  • 16
13

An array is implicitly convertable to a pointer, and pointer-to-reference is illegal in C++

EFraim
  • 12,811
  • 4
  • 46
  • 62
  • 11
    It is true you cannot have a pointer to a reference, but this is not the reason that you cannot have an array of references. Rather they are both symptoms of the fact that references are not objects. – Richard Corden Jul 22 '09 at 10:17
  • 3
    A struct can contain nothing but references, and it will have a size proportional to the number of them. If you did have an array of refs 'arr', the normal array-to-pointer conversion would not make sense, since 'pointer-to-reference' doesn't make sense. *but* it would make sense to just use arr[i] , in the same way as st.ref when 'st' is a struct containing a ref. Hmm. But &arr[0] would give the address of the first referenced object, and &arr[1]- &arr[0] would not be the same as &arr[2]-&arr[1] - it would create a lot of strangeness. – greggo Nov 10 '11 at 23:56
10

Because like many have said here, references are not objects. they are simply aliases. True some compilers might implement them as pointers, but the standard does not force/specify that. And because references are not objects, you cannot point to them. Storing elements in an array means there is some kind of index address (i.e., pointing to elements at a certain index); and that is why you cannot have arrays of references, because you cannot point to them.

Use boost::reference_wrapper, or boost::tuple instead; or just pointers.

greggo
  • 3,009
  • 2
  • 23
  • 22
navigator
  • 1,588
  • 1
  • 13
  • 19
7

I believe the answer is very simple and it has to do with semantic rules of references and how arrays are handled in C++.

In short: References can be thought of as structs which don't have a default constructor, so all the same rules apply.

1) Semantically, references don't have a default value. References can only be created by referencing something. References don't have a value to represent the absence of a reference.

2) When allocating an array of size X, program creates a collection of default-initialized objects. Since reference doesn't have a default value, creating such an array is semantically illegal.

This rule also applies to structs/classes which don't have a default constructor. The following code sample doesn't compile:

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

Object objects[1]; // Error: no appropriate default constructor available
Kristupas A.
  • 304
  • 5
  • 15
  • 5
    You can create an array of `Object`, you just have to make sure to initialize them all: `Object objects[1] = {Object(42)};`. Meanwhile, you can't create an array of references, even if you initialize all of them. – Anton3 Mar 30 '20 at 16:39
4

You can get fairly close with this template struct. However, you need to initialize with expressions that are pointers to T, rather than T; and so, though you can easily make a 'fake_constref_array' similarly, you won't be able to bind that to rvalues as done in the OP's example ('8');

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

greggo
  • 3,009
  • 2
  • 23
  • 22
4

Just to add to all the conversation. Since arrays requires consecutive memory locations to store the item, so if we create an array of references then it's not guaranteed that they will be at consecutive memory location so accessing will be a problem and hence we can't even apply all the mathematical operations on array.

Amit Kumar
  • 51
  • 1
  • Consider the below code from the original question: int a = 1, b = 2, c = 3; int& arr[] = {a,b,c,8}; There's very less possibility that a,b,c will reside at consecutive memory location. – Amit Kumar Nov 29 '15 at 18:12
  • That is far from the main problem with the concept of holding references in a container. – underscore_d Apr 20 '17 at 08:37
2

A reference object has no size. If you write sizeof(referenceVariable), it will give you the size of the object referenced by referenceVariable, not that of the reference itself. It has no size of its own, which is why the compiler can't calculate how much size the array would require.

Carl Seleborg
  • 13,125
  • 11
  • 58
  • 70
  • 3
    if so how then a compiler can calculate size of a struct containing only a ref? – Juster Sep 12 '15 at 09:56
  • Compiler does know the physical size of a reference - its the same as a pointer. References are, in fact, semantically glorified pointers. The difference between pointers and references is that references have quite a few semantic rules placed on them in order to reduce the number of bugs you might create as compared with pointers. – Kristupas A. Apr 21 '18 at 10:32
2

When you store something in an array , its size needs to be known (since array indexing relies on the size). Per the C++ standard It is unspecified whether or not a reference requires storage, as a result indexing an array of references would not be possible.

Pradyot
  • 2,897
  • 7
  • 41
  • 58
  • 1
    But by putting said reference in a `struct`, magically everything becomes specified, well-defined, guaranteed, and of deterministic size. Why, therefore, does this seemingly totally artificial difference exist? – underscore_d Jul 22 '16 at 06:28
  • ...of course, if one actually starts to think about what the wrapping `struct` confers, some good possible answers begin to suggest themselves - but to me, no one has done so yet, despite this being one of the immediate challenges to all the common rationales for why you can't use unwrapped refs. – underscore_d Jul 22 '16 at 06:47
0

Consider an array of pointers. A pointer is really an address; so when you initialize the array, you are analogously telling the computer, "allocate this block of memory to hold these X numbers (which are addresses of other items)." Then if you change one of the pointers, you are just changing what it points to; it is still a numerical address which is itself sitting in the same spot.

A reference is analogous to an alias. If you were to declare an array of references, you would basically be telling the computer, "allocate this amorphous blob of memory consisting of all these different items scattered around."

Dan Tao
  • 125,917
  • 54
  • 300
  • 447
  • 1
    Why, then, does making a `struct` whose only member is a reference result in something completely legal, predictable, and non-amorphous? Why must a user wrap a reference for it to magically gain array-capable powers, or is it just an artificial restriction? IMO no answer has directly addressed this. I assume the rebuttal is 'The wrapping object ensures you _can_ use value semantics, in the form of the wrapping object, so it ensures you can have the guarantees of values, which are needed because e.g. how would one disambiguate between element and referee address'... Is that what you meant? – underscore_d Jul 22 '16 at 06:44
-2

Actually, this is a mixture of C and C++ syntax.

You should either use pure C arrays, which cannot be of references, since reference are part of C++ only. Or you go the C++ way and use the std::vector or std::array class for your purpose.

As for the edited part: Even though the struct is an element from C, you define a constructor and operator functions, which make it a C++ class. Consequently, your struct would not compile in pure C!

Thargon
  • 424
  • 1
  • 3
  • 12
  • 1
    What's your point? `std::vector` or `std::array` cannot contain references either. The C++ Standard is quite explicit that no container can. – underscore_d Jul 22 '16 at 06:31