Passing array by ref - c++

Ref. to my last post and sellibitze's comment to that post on passing array by ref rather than by value, why is it that when I'm passing array by value compiler can deduce arguments but it won't do it if I pass it by value?
template<class T,int row, int col>
void invert(T (&a)[row][col]) //NOTE AMPERSAND
in main with declaration above I can call:
int main(int argc, char* argv[])
{
invert(a);//HERE ARGUMETS ARE AUTOMATICALLY DEDUCED
}
but without ampersand I would have to call it like so:
int main(int argc, char* argv[])
{
invert<int,3,4>(a);
}
#Paul So just to make it clear when I'm declaring fnc:
void f(int a[]);//I'm passing a pointer
but when I'm declaring:
void f(int &a[]);//I'm passing a ref?
Do I understand this correctly now?

That's because when you pass an array "by value" it decays to a pointer. That is, you are in fact passing a pointer to the first element without any size information.
When you have a signature like this:
void foo(int arr[10]);
then the value 10 is completely ignored and you can pass arrays of ints of any size to it. It is exactly the same as
void foo(int arr[]);
or
void foo(int* arr);
As you can see the size information is not preserved, and therefore it cannot be used to deduce the size of the array.
With a two-dimensional array, the first dimension decays. For example: an array of 10 arrays of 20 integers (int arr[10][20]) decays to a pointer to arrays of 20 integers (int (*arr)[20]) etc, so the value 10 cannot be deduced but the size of the second dimension (20) is preserved an can be deduced.
template<class T,int row, int col>
void foo(T (&a)[row][col]) { }
template <class T, int col>
void bar(T arr[][col]) {}
int main()
{
int a[10][20];
foo(a);
bar(a);
}
When you pass something by reference, the type is preserved, the arrays don't decay and all the size information will remain available.

You can't pass array by value, only by pointer (when you pass an array to a function it's automatically converted to a pointer).
I don't really understand what you mean by "deduce arguments"; do you mean the total size of the array? If yes then if you pass it by pointer it's lost because pointers don't carry that kind of information.
Anyways I highly recommend to use std::vector rather than plain old C arrays; many less headaches! They are passed by value by default (as you would expect); they can easily passed by reference and by pointer if you want to, and they never lose information such as the array size. They also protected against buffer overflows and underflows, and they automatically grow as you add more elements.

void f(int &a[]); // I'm passing a ref?
No, here you try to pass an array of references, which does not exist in the type system. You might be tempted to write the following:
void f(int (&a)[]);
But references to arrays of unknown bound are not allowed as function parameters.
When you declare a function parameter as an array of n dimensions, it is rewritten by the compiler as a pointer to an array of n-1 dimensions. The following signatures are equivalent:
void fun(int x[][10]);
void fun(int x[2][10]);
void fun(int x[99][10]);
void fun(int (*x)[10]);
The bound of the first dimension is ignored and thus cannot be inferred by the template mechanism.
What you can do is pass a pointer to the entire 2D array:
template <class T, int row, int col>
void invert(T (*a)[row][col])
{
std::cout << row << " x " << col << std::endl;
T first_entry = (*a)[0][0];
}
int main(int argc, char* argv[])
{
int a[10][20];
invert(&a);
}
This works as expected, but as you can see, the syntax is a little clumsy on both sides.

Related

How do I pass an array as an reference to a pointer of const char? [duplicate]

How does passing a statically allocated array by reference work?
void foo(int (&myArray)[100])
{
}
int main()
{
int a[100];
foo(a);
}
Does (&myArray)[100] have any meaning or its just a syntax to pass any array by reference?
I don't understand separate parenthesis followed by big brackets here. Thanks.
It's a syntax for array references - you need to use (&array) to clarify to the compiler that you want a reference to an array, rather than the (invalid) array of references int & array[100];.
EDIT: Some clarification.
void foo(int * x);
void foo(int x[100]);
void foo(int x[]);
These three are different ways of declaring the same function. They're all treated as taking an int * parameter, you can pass any size array to them.
void foo(int (&x)[100]);
This only accepts arrays of 100 integers. You can safely use sizeof on x
void foo(int & x[100]); // error
This is parsed as an "array of references" - which isn't legal.
It's just the required syntax:
void Func(int (&myArray)[100])
^ Pass array of 100 int by reference the parameters name is myArray;
void Func(int* myArray)
^ Pass an array. Array decays to a pointer. Thus you lose size information.
void Func(int (*myFunc)(double))
^ Pass a function pointer. The function returns an int and takes a double. The parameter name is myFunc.
It is a syntax. In the function arguments int (&myArray)[100] parenthesis that enclose the &myArray are necessary. if you don't use them, you will be passing an array of references and that is because the subscript operator [] has higher precedence over the & operator.
E.g. int &myArray[100] // array of references
So, by using type construction () you tell the compiler that you want a reference to an array of 100 integers.
E.g int (&myArray)[100] // reference of an array of 100 ints
The following creates a generic function, taking an array of any size and of any type by reference:
template<typename T, std::size_t S>
void my_func(T (&arr)[S]) {
// do stuff
}
play with the code.
Arrays are default passed by pointers. You can try modifying an array inside a function call for better understanding.

Does the size of a pointer to an array matter when passed as a parameter to a function [duplicate]

I don't understand why the following example compiles and works:
void printValues(int nums[3], int length) {
for(int i = 0; i < length; i++)
std::cout << nums[i] << " ";
std::cout << '\n';
}
It seems that the size of 3 is completely ignored but putting an invalid size results in a compile error. What is going on here?
In C++ (as well as in C), parameters declared with array type always immediately decay to pointer type. The following three declarations are equivalent
void printValues(int nums[3], int length);
void printValues(int nums[], int length);
void printValues(int *nums, int length);
I.e. the size does not matter. Yet, it still does not mean that you can use an invalid array declaration there, i.e. it is illegal to specify a negative or zero size, for example.
(BTW, the same applies to parameters of function type - it immediately decays to pointer-to-function type.)
If you want to enforce array size matching between arguments and parameters, use pointer- or reference-to-array types in parameter declarations
void printValues(int (&nums)[3]);
void printValues(int (*nums)[3]);
Of course, in this case the size will become a compile-time constant and there's no point of passing length anymore.
I don't see what compile error you are referring to - arrays passed to a function decay to pointers and you lose the array type information. You might as well have used:
void printValues(int* nums, int length);
You can avoid the decay to pointers by using references:
void printValues(int (&nums)[3], int length);
Or simply use pointers if you don't want fixed-sized arrays.
The size of the array is not ignored, it is part of the type of the argument. You should get a compiler error if you try to pass an array of any other size into the function.
On the other hand C and C++ don't do bounds checking on array accesses, so in that sense they are ignored. But that's true in any other context as well, not just for function parameters.

Template function array loop [duplicate]

How does passing a statically allocated array by reference work?
void foo(int (&myArray)[100])
{
}
int main()
{
int a[100];
foo(a);
}
Does (&myArray)[100] have any meaning or its just a syntax to pass any array by reference?
I don't understand separate parenthesis followed by big brackets here. Thanks.
It's a syntax for array references - you need to use (&array) to clarify to the compiler that you want a reference to an array, rather than the (invalid) array of references int & array[100];.
EDIT: Some clarification.
void foo(int * x);
void foo(int x[100]);
void foo(int x[]);
These three are different ways of declaring the same function. They're all treated as taking an int * parameter, you can pass any size array to them.
void foo(int (&x)[100]);
This only accepts arrays of 100 integers. You can safely use sizeof on x
void foo(int & x[100]); // error
This is parsed as an "array of references" - which isn't legal.
It's just the required syntax:
void Func(int (&myArray)[100])
^ Pass array of 100 int by reference the parameters name is myArray;
void Func(int* myArray)
^ Pass an array. Array decays to a pointer. Thus you lose size information.
void Func(int (*myFunc)(double))
^ Pass a function pointer. The function returns an int and takes a double. The parameter name is myFunc.
It is a syntax. In the function arguments int (&myArray)[100] parenthesis that enclose the &myArray are necessary. if you don't use them, you will be passing an array of references and that is because the subscript operator [] has higher precedence over the & operator.
E.g. int &myArray[100] // array of references
So, by using type construction () you tell the compiler that you want a reference to an array of 100 integers.
E.g int (&myArray)[100] // reference of an array of 100 ints
The following creates a generic function, taking an array of any size and of any type by reference:
template<typename T, std::size_t S>
void my_func(T (&arr)[S]) {
// do stuff
}
play with the code.
Arrays are default passed by pointers. You can try modifying an array inside a function call for better understanding.

Why doesn't this template function call work inside this function?

The following code doesn't compile, I am trying to figure out how to calculate the size of an array that is passed into a function and can't seem to get the syntax correct.
The error I am getting is :
Error 1 error C2784: 'size_t getSize(T (&)[SIZE])' : could not deduce template argument for 'T (&)[SIZE]' from 'const byte []' 16 1 sizeofarray
Here is the source code:
#include <cstdint>
#include <stdio.h>
template<typename T, size_t SIZE>
size_t getSize(T (&)[SIZE]) {
return SIZE;
}
typedef std::uint_fast8_t byte;
void processArray(const byte b[])
{
size_t size = getSize(b); // <- line 16 where error occurs
// do some other stuff
}
int main(const int argc, const char* argv[])
{
byte b[] = {1,2,3,4,5,6};
printf("%u\n", getSize(b));
processArray(b);
return 0;
}
If you want this to work, you need to make processArray be a template as well:
template <size_t size>
void processArray(const byte (&b)[size])
{
// do some other stuff
}
C++ does not allow passing arrays by value. If you have a function like this:
void f(int a[5]);
It may look like you are passing an array by value, but the language has a special rule that says a parameter of this form is just another way of saying:
void f(int *a);
So the size of the array is not part of the type at all. This is behavior inhereted from C. Fortunately, C++ has references, and you can pass a reference to an array, like this:
void f(int (&a)[5]);
This way, the size of your array is preserved.
Now, the only remaining trick is to make the function generic, so it can work on any size array.
template <size_t n> void f(int (&a)[n]);
Now, new versions of the function that take references to arrays of different sizes can be generated automatically for you, and the size can be accessed through the template parameter.
As the argument to a function, const byte b[] is treated just like const byte *b. There is no compile-time information about the size of the array that the function was called with.
To pass a reference to the array, you need to make processArray a template and use the same technique. If you don't pass in a reference, the parameter is a pointer, and pointer types don't have array size information.
template<size_t size>
void processArray(const byte (&b)[size]) {
// ...
}
This is the canonical example of why you should use a function template like getSize rather than sizeof, for determining array size.
With sizeof, you'd have gotten the size of a pointer and been none the wiser.
But, this way, you get a compilation error to point out your mistake.
What was the mistake? It was having a function parameter const T arg[] and thinking that this means arg is an array. It's not. This is unfortunate syntax from C that is precisely equivalent to const T* arg. You get a pointer.
This is also why some people think — incorrectly — that "arrays are pointers". They are not. It's just this specific syntax.

Different sizeof results

Why does n not equal to 8 in the following function?
void foo(char cvalue[8])
{
int n = sizeof (cvalue);
}
But n does equal to 8 in this version of the function:
void bar()
{
char cvalue[8];
int n = sizeof (cvalue);
}
Because you can't pass entire arrays as function parameters in C. You're actually passing a pointer to it; the brackets are syntactic sugar. There are no guarantees the array you're pointing to has size 8, since you could pass this function any character pointer you want.
// These all do the same thing
void foo(char cvalue[8])
void foo(char cvalue[])
void foo(char *cvalue)
C and C++ arrays are not first class objects; you cannot pass arrays to functions, they always decay to pointers.
You can, however, pass pointers and references to arrays. This prevents the array bounds from decaying. So this is legal:
template<typename T, size_t N>
void foo(const T(&arr)[N])
{
int n = sizeof(arr);
}
In the first example, cvalue as passed parameter is in really just a pointer to a character array and when you take the sizeof() of it, you get the size of the pointer. In the second case, where you've declared it as a local variable, you get the size of the the entire array.
The size of the parameter on 32-bit systems will be 4 and on 64-bit systems compiled with -m64 will be 8. This is because arrays are passed as pointers in functions. The pointer is merely a memory address.