开发者

What is useful about a reference-to-array parameter?

I recently found some code like this:

typedef int TenInts[10];
void foo(TenInts &arr);

What can you do in the body of foo() that is useful, that you could not do if the declaration was:

开发者_StackOverflow社区
void foo(int *arr);    // or,
void foo(int arr[]);   // or,
void foo(int arr[10]); // ?

I found a question that asks how to pass a reference to an array. I guess I am asking why.

Also, only one answer to "When is pointer to array useful?" discussed function parameters, so I don't think this is a duplicate question.


The reference-to-array parameter does not allow array type to decay to pointer type. i.e. the exact array type remains preserved inside the function. (For example, you can use the sizeof arr / sizeof *arr trick on the parameter and get the element count). The compiler will also perform type checking in order to make sure the array argument type is exactly the same as the array parameter type, i.e. if the parameter is declared as a array of 10 ints, the argument is required to be an array of exactly 10 ints and nothing else.

In fact, in situations when the array size is fixed at compile-time, using a reference-to-array (or pointer-to-array) parameter declarations can be preceived as the primary, preferred way to pass an array. The other variant (when the array type is allowed to decay to pointer type) are reserved for situations when it is necessary to pass arrays of run-time size.

For example, the correct way to pass an array of compile-time size to a function is

void foo(int (&arr)[10]); // reference to an array

or

void foo(int (*arr)[10]); // pointer to an array

An arguably incorrect way would be to use a "decayed" approach

void foo(int arr[]); // pointer to an element
// Bad practice!!!

The "decayed" approach should be normally reserved for arrays of run-time size and is normally accompanied by the actual size of the array in a separate parameter

void foo(int arr[], unsigned n); // pointer to an element
// Passing a run-time sized array

In other words, there's really no "why" question when it comes to reference-to-array (or pointer-to-array) passing. You are supposed to use this method naturally, by default, whenever you can, if the array size is fixed at compile-time. The "why" question should really arise when you use the "decayed" method of array passing. The "decayed" method is only supposed to be used as a specialized trick for passing arrays of run-time size.

The above is basically a direct consequence of a more generic principle. When you have a "heavy" object of type T, you normally pass it either by pointer T * or by reference T &. Arrays are no exception from this general principle. They have no reason to be.

Keep in mind though that in practice it is often makes sense to write functions that work with arrays of run-time size, especially when it comes to generic, library-level functions. Such functions are more versatile. That means that often there's a good reason to use the "decayed" approach in real life code, Nevertheless, this does not excuse the author of the code from recognizing the situations when the array size is known at compile time and using the reference-to-array method accordingly.


One difference is that it's (supposed to be) impossible to pass a null reference. So in theory the function does not need to check if the parameter is null, whereas an int *arr parameter could be passed null.


You can write a function template to find out the size of an array at compile time.

template<class E, size_t size>
size_t array_size(E(&)[size])
{
    return size;
}

int main()
{
    int test[] = {2, 3, 5, 7, 11, 13, 17, 19};
    std::cout << array_size(test) << std::endl; // prints 8
}

No more sizeof(test) / sizeof(test[0]) for me ;-)


Shouldn't we also address the words in bold from the question:

What can you do in the body of foo() that is useful, that you could not do if the declaration was void foo(int arr[]);?

The answer is: nothing. Passing an argument by reference allows a function to change its value and pass back this change to the caller. However, it is not possible to change the value of the array as a whole, which would have been a reason to pass it by reference.

void foo(int (&arr)[3]) { // reference to an array
   arr = {1, 2 ,3};       // ILLEGAL: array type int[3] is not assignable
   arr = new(int[3]);     // same issue
   arr = arr2;            // same issue, with arr2 global variable of type int[3] 
}


You can ensure that the function is only called on int arrays of size 10. That may be useful from a type-checking standpoint.


You get more semantic meaning regarding what the function is expecting.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜