103

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.

6
  • 39
    Check the clockwise spiral rule. Commented Apr 4, 2012 at 9:03
  • 1
    have you considered using a std::vector or similar? Commented Apr 4, 2012 at 9:06
  • 2
    @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. Commented Dec 11, 2018 at 11:52
  • also look into en.cppreference.com/w/cpp/utility/functional/reference_wrapper Commented Mar 25, 2019 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. Commented Nov 5, 2019 at 0:30

7 Answers 7

175

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.

Sign up to request clarification or add additional context in comments.

5 Comments

"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 & );.
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".
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<>)?
@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)
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.
22

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.

5 Comments

The second one doesn't compile for me.
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.)
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 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.)
Maybe this answer should be fixed to reflect this?
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) {}

Also have a look at std::array if the size of the array is known and static.

With C++20 support, also consider to wrap and pass the data as a std::span. Note that there might be some peculiarities to be aware of when it comes to constness.

For reference:

2 Comments

:-) Yes. std::vector is usually the best solution. But there are exceptions.
As of C++ 20, std::span would be preferable, it is more flexible than std::vector if there is no need for growing of the array.
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.

3 Comments

Except that you cannot pass an array to the latter. He asked about arrays, not pointers.
@JamesKanze: true. The second case won't work on arrays on stack, in that case, he should use the first form.
The second case won't work for arrays, period. It will only work if you have an actual variable of pointer type.
5

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;

Comments

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.

1 Comment

Changing the order still doesn't allow him to pass an array. It requires an actual pointer object, not the results of a conversion.
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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.