When the compiler builds a pointer out of an array, that pointer is an rvalue: a non-const lvalue reference cannot be bound to it.
It's the same as with any other implicit conversion:
1 2 3 4 5 6 7 8 9
void byValue(int n) {}
void byReference(int& n) {}
int main()
{
double d = 2.0;
byValue(d); // OK
byReference(d); // error: cannot bind a reference to the rvalue int
}
You can capture that value by using a const lvalue reference
1 2 3
void byReference(int** const & PtoP)
{
}
or an rvalue reference:
1 2 3
void byReference(int**&& PtoP)
{
}
For the third ("byAddress") case, you just have two totally different pointer types: pointer to array of three pointers to int vs pointer to pointer to pointer to int.
&myArray is a pointer to an array of three pointers to int. That is not int***.
Ok, but!
my is an array of ints (1 level of indirection *) my2 in an array of ints (1 level of indirection *)
RANDOM is a pointer to pointer (2 levels of indirection **)
myArray is an array of pointers
-> can be translated to ->
myArray is a pointer to the first of the 3 pointers (in short, it's a pointer to RANDOM, which in turn is a pointer) -----> (2 levels of indirection **)
We know that adding & before a variable, we can get the pointer to that variable, making ANOTHER level of indirection
1 2 3 4 5
// pointer originally has 1 level of indirection...
int* pointer;
// but, by obtaining its address, we reach another level of indirection (which is the pointerToPointer)
int** pointerToPointer = &pointer;
Isn't it the same thing with the myArray?
Originally myArray is a pointer-to-pointer.
Adding another level of indirection to it (by adding &), we should get a pointer-to-pointer-to-pointer! (3 levels of indirection!!!)
No you can't because that pointer is "chained" to the block of memory of the array. So, as you said in your previous post, I can't change the memory the pointer is pointing to.
However, how could I pass myArray to a function like byAddress() ?
2) Illegal. We can't modify an array 'fake' pointer to point to something else.
It isn't fake, it's a very real pointer value constructed at the point of call as if by &hel[0]. You can modify it if you change that function to void _ref(int*&& p)
3) Just illegal because I'm trying to add a new level of indirection on a not-pointer type (hel)
you can add another level of indirection to any non-reference type. This is invalid because pointer to array and pointer to pointer are different types and there is no implicit conversion between them.
4) Legal. What's the difference with this function and number #1 ?
#1 takes a pointer by value, #4 takes an array by reference. What isn't different between them? Within #4, the type of the parameter is int[5]: you can check it with sizeof, you can apply std::begin, std::rank, std::extent, and other array-only functions, you can iterate it using a range-for loop. Can't do those things with a pointer.
1 2 3 4 5 6 7 8 9 10 11 12
#include <iostream>
void _RealAddr(int (&array)[5])
{
for(int n: array) // in void _val(int* p), for(int n: p) won't compile
std::cout << n << ' ';
}
int main()
{
int hel[5] = {};
_RealAddr(hel);
}
2) those are not actually 3 functions: those are three declarations for the same function with type void(int*). You can use one form for declaration and another for the matching definition. This parameter type rewriting for certain types is one of the most confusing things C invented (and C++ inherited). See http://en.cppreference.com/w/cpp/language/function#Parameter_list from "Because of these rules, the following function declarations declare exactly the same function:"
And yes, a function call to a function taking a pointer, however declared, with an argument of array type will cause a pointer to that array's first element to be created prior to function call (assuming such pointer would be same or convertible to the function's parameter)
But of course the size must be explicit.
It can be a deduced template parameter. There's about a dozen function templates in the C++ standard library that take arrays by reference.