Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Syntax for returning an array reference in C++

I have been brushing up on array concepts in C++, when I came across this question: Return an array in c++

Someone answered using this declaration:

int (&f(int (&arr)[3]))[3]

What I can't seem to grasp is the [3] after the closing parenthesise. I've never seen a function declaration that looked like this. I understand the rest of the syntax, but I don't particularly understand how the [3] works because it is after the function name. I apologize in advance if I'm overlooking something simple. I also tried looking at the spec for function declarations but I didn't see anything related that I could link to the subscript syntax. So, how is this possible?

like image 631
Izodine Avatar asked Sep 23 '15 06:09

Izodine


2 Answers

The function is returning a reference to an array of int of size 3 and the [3] part after the function actually the size of the array to be returned as reference.

This obscure syntax comes from the weird syntax of array declaration, which you do as:

int arr[3]; //real but weird

The language would have been much simpler if it had this instead:

int[3] arr; //hypothetical but better and simpler

because size 3 is part of the type of arr, so it makes much more sense if all the parts appear on the left side of the variable name, in the same way as when you write:

unsigned int a;

You dont write:

unsigned a int; //analogous to : int a [3];

So while the language does the right thing with unsigned int, it does a very weird thing with int[3].

Now coming back to the function declaration, the function would have been much better if it is declared as:

int[3]& f(int[3]& arr); //hypothetical

only if it had all the parts on the left side of variable-name. But since it doesn't do that (i.e the language requires you to write the size on the rightmost side after the variable name), you end up with this weird signature:

int (&f(int (&arr)[3])[3]; //real

Notice that how even the parameter becomes weird.


But you can simplify it with a typedef as:

typedef int array_type[3];

array_type& f(array_type& arr);

That looks much better. Now only the typedef looks weird.

With C++11, you can write even a better typedef:

using array_type = int[3];

array_type& f(array_type& arr);

which is as close as this (if you visualize array_type as int[3]):

int[3]& f(int[3]& arr); //hypothetical

Hope that helps.

like image 179
Nawaz Avatar answered Oct 27 '22 05:10

Nawaz


Well, read this article about C history, by its creator Dennis M. Ritchie.

Looks like this syntax we got from B, thru C to C++...

I believe, this way of declaring several variables of some "basic type" is a root of this strange syntax:

int a, *b, c[3];

So a is just int, b is pointer to int and c is array of int...

If these * and [3] would be part of type definition not a variable definition - then we would need 3 lines to write this:

int a;
int* b;
int[3] c;

There are two good resources on SO C page to learn how to parse such constructions:

  • The Clockwise/Spiral Rule for parsing C declarations.
  • cdecl: C gibberish ↔ English, a site that translate C expressions to readable English.

cdecl is failing a little - because references are not part of C - so let's try with The Clockwise/Spiral Rule for parsing C declarations.

int (&f(int (&arr)[3]))[3]
  1. arr -> arr
  2. &arr -> arr is reference
  3. (&arr) -> arr is reference - surrounding () not really change a thing
  4. (&arr)[3] -> arr is reference to array of size 3
  5. int (&arr)[3] -> arr is reference to array of size 3 of type int

Next:

  1. f -> f
  2. f(int (&arr)[3]) -> f is function taking arr, which is ...
  3. &f(int (&arr)[3]) -> f is function taking arr, which is ..., and returning reference
  4. (&f(int (&arr)[3]))[3] -> f is function taking arr, which is ..., and returning reference to array of size 3
  5. int (&f(int (&arr)[3]))[3] -> f is function taking arr, which is reference to array of size 3 of type int, and returning reference to array of size 3 of type int

Of course - this code shall be replaced with, at least for me, much easier to read version:

using int3 = int[3];
int3& f(int3& arr);
like image 3
PiotrNycz Avatar answered Oct 27 '22 04:10

PiotrNycz