97

is this allowed to pass an array by reference ?

 void foo(double& *bar) 

Seems that my compiler says no. Why? What is the proper way to pass an array by reference? Or a work around? I have an array argument that my method should modify and that I should retrieve afterwards. Alternatively, I could make this array a class member, which works fine, but it has many drawbacks for other part of my code (that I would like to avoid).

Thanks and regards.

BugShotGG
  • 5,008
  • 8
  • 47
  • 63
kiriloff
  • 25,609
  • 37
  • 148
  • 229
  • 38
    Check the [clockwise spiral rule](http://c-faq.com/decl/spiral.anderson.html). – Some programmer dude Apr 04 '12 at 09:03
  • 1
    have you considered using a `std::vector` or similar? – moooeeeep Apr 04 '12 at 09:06
  • 1
    @moooeeeep `std::vector` is obviously superior if the size is dynamic/unknown, but in cases where the size is always the same, it would be overkill, and instead `std::array` resolves the syntactic ugliness while also conferring value semantics. – underscore_d Dec 11 '18 at 11:52
  • also look into https://en.cppreference.com/w/cpp/utility/functional/reference_wrapper – sdevikar Mar 25 '19 at 15:46
  • why not just void foo(double bar[ ]) ? if you wish to, you could also supply the length of array as void foo(double bar[2]). Unless for a complex type a copy constructor needs to be avoided. – ultimate cause Nov 05 '19 at 00:30
  • @ultimatecause That does not declare/pass an array argument, just a pointer to the first element. Including a size makes no difference; it gets discarded. Even if it worked how you thought, you'd not be passing by reference, but by value, which is wasteful and pointless. – underscore_d Apr 12 '21 at 08:23

7 Answers7

166

Arrays can only be passed by reference, actually:

void foo(double (&bar)[10])
{
}

This prevents you from doing things like:

double arr[20];
foo(arr); // won't compile

To be able to pass an arbitrary size array to foo, make it a template and capture the size of the array at compile time:

template<typename T, size_t N>
void foo(T (&bar)[N])
{
    // use N here
}

You should seriously consider using std::vector, or if you have a compiler that supports c++11, std::array.

jrok
  • 54,456
  • 9
  • 109
  • 141
  • 7
    "Still, once passed to `foo`, the array decays to pointer". No it doesn't, and since you can only pass an array of 10 elements, you don't need further information about the size. (And once again, you _cannot_ pass an array to `void foo( double*& )`; the results of an implicit conversion is an rvalue, and cannot be used to initialize a reference to non-const. You must use `void foo( double *const & );`. – James Kanze Apr 04 '12 at 09:40
  • 1
    great :) this is exactly what I looking for. I was doing `void foo(T &bar[N])` in my template - but that actually means "an array of references" and not "reference to array". – OLL Nov 05 '15 at 11:23
  • If such a function already exists, and all you have is a `std::vector` (of the correct size), how would you cast the contents of the vector (perhaps obtained via `std::vector::data()`) to this array type? What's the syntax for a fixed array size in a type parameter (i.e. to use with `static_cast<>`)? – davidA Aug 14 '17 at 02:52
  • 1
    @meowsqueak You wouldn't, because the cast would have undefined behaviour, AFAICT; I can see no allowance made in `reinterpret_cast` for treating a dynamically allocated array (with no intrinsic size after allocation) as an automatic one with fixed size. Besides, that's the wrong question: the right question would be 'How can I refactor my code to work with both types of container?', and the answer is templates and iterators/ranges, or since you said the size is fixed, a function that takes only a data pointer/ref and hard-codes the size (or if size could vary, a data/size adaptor like `span`) – underscore_d Dec 11 '18 at 11:47
  • why not just void foo(double bar[ ])? if you wish to, you could also supply the length of array as void foo(double bar[2]). Unless for a complex type a copy constructor needs to be avoided. – ultimate cause Nov 05 '19 at 00:30
19

Yes, but when argument matching for a reference, the implicit array to pointer isn't automatic, so you need something like:

void foo( double (&array)[42] );

or

void foo( double (&array)[] );

Be aware, however, that when matching, double [42] and double [] are distinct types. If you have an array of an unknown dimension, it will match the second, but not the first, and if you have an array with 42 elements, it will match the first but not the second. (The latter is, IMHO, very counter-intuitive.)

In the second case, you'll also have to pass the dimension, since there's no way to recover it once you're inside the function.

Vilhelm Gray
  • 11,516
  • 10
  • 61
  • 114
James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • 8
    The second one doesn't compile for me. – jrok Apr 04 '12 at 09:29
  • For me neither. I don't know if it's due to a compiler bug or something in the standard I've overlooked. I've never actually wanted to do it. (The first is very useful if the dimension is a template parameter, since the compiler will figure out the correct dimension for you.) – James Kanze Apr 04 '12 at 09:37
  • 6
    I do not believe that the second example is valid Standard conforming C++. Incidentally, it doesn't compile with GCC 4.8 or Clang 3.4. I think a templated size is required. – Ricky65 May 18 '14 at 12:13
  • 3
    @Ricky65 So it is. I wonder why. References to incomplete types are normally allowed as function parameters. (Of course, the in this case, the type changes when it is completed: `double []` and `double [42]` are two different types. So the only thing you could pass to the latter form would be an array of unknown length, e.g. `extern double d[];`, or some such. Which seriously limits the usability.) – James Kanze May 19 '14 at 08:24
  • 2
    Maybe this answer should be fixed to reflect this? – CoffeeTableEspresso Jun 22 '22 at 17:40
8

As you are using C++, the obligatory suggestion that's still missing here, is to use std::vector<double>.

You can easily pass it by reference:

void foo(std::vector<double>& bar) {}

And if you have C++11 support, also have a look at std::array.

For reference:

moooeeeep
  • 31,622
  • 22
  • 98
  • 187
6

If you want to modify just the elements:

void foo(double *bar);

is enough.

If you want to modify the address to (e.g.: realloc), but it doesn't work for arrays:

void foo(double *&bar);

is the correct form.

Naszta
  • 7,560
  • 2
  • 33
  • 49
3

Here, Erik explains every way pass an array by reference: https://stackoverflow.com/a/5724184/5090928.

Similarly, you can create an array reference variable like so:

int arr1[] = {1, 2, 3, 4, 5};
int(&arr2)[5] = arr1;
serg06
  • 2,027
  • 1
  • 18
  • 26
2

Like the other answer says, put the & after the *.

This brings up an interesting point that can be confusing sometimes: types should be read from right to left. For example, this is (starting from the rightmost *) a pointer to a constant pointer to an int.

int * const *x;

What you wrote would therefore be a pointer to a reference, which is not possible.

parkovski
  • 1,503
  • 10
  • 13
  • Changing the order still doesn't allow him to pass an array. It requires an actual pointer object, not the results of a conversion. – James Kanze Apr 04 '12 at 09:29
2

8.3.5.8 If the type of a parameter includes a type of the form “pointer to array of unknown bound of T” or “reference to array of unknown bound of T,” the program is ill-formed

Kirilodius
  • 136
  • 1
  • 5