Here is the case of using std::swap to interchange values of two arrays.
int arr1[3]={1,2,3};
int arr2[3]={4,5,6};
std::swap(arr1,arr2);
//Then arr1 becomes {4,5,6} and arr2 becomes {1,2,3}
The swap function is declared as
template <class T, size_t N> void swap(T (&a)[N], T (&b)[N])
noexcept (noexcept(swap(*a,*b)));
I am curious about the mechanisms of the size_t N deduction, how is it accomplished? Since a pointer of an int array doesn't have any information about its length.
A reference to array is only allowed to bind to an array of the correct extent. For example a int (&)[10] cannot bind to an int array of size other than 10, nor can it bind to an int*. This is also true when you have a parameter of reference to array type.
When you pass an array to a function and the parameter is not a reference, the argument is decayed to a pointer to the array's first element. But when you pass an array by reference, for the purposes of template parameter deduction, the argument type is not decayed. This is because a reference to array parameter can't bind to a pointer. Since the argument type isn't decayed, the template parameter N can be correctly deduced.
Related
void func1(int* ptr)
{
printf("func1 :%d\n",++ptr);
}
int main()
{
int arr[4] = {0,1,2,3};
printf("Addr enter code here`f arr: %d\n",arr);
func1(arr); // first way:
func1(&arr); // second way: How this will be different from func1(arr).
}
The difference between &arr and arr is that the type of former is a pointer to an array int (*)[4], and the type of latter is an array int[4] which can decay to pointer to the first element int*. Both the pointer to the array, and the decayed pointer to the first element point to the same address because the first byte of the array is also the first byte of the first element of the array.
The difference between func1(&arr) and func1(arr) is that former is ill-formed because int (*)[4] doesn't implicitly convert to int*. In order to be able to call func1(&arr), you would have to accept a pointer of correct type:
void func1(int (*ptr)[4])
printf("func1 :%d\n",++ptr);
printf("Addr enter code here`f arr: %d\n",arr);
Both of these calls result in undefined behaviour. The type of the variadic argument must match the type required by the format specifier. You used the format specifier %d which requires the argument to be of type int (or similar). The type of the (decayed) argument here is int* which isn't int.
Arrays decay to pointer. Any of those pointers reference the same address in memory. The difference is in the type of this pointer.
int arr[5];
arr and &arr[0] have type pointer to int (int *)
&arr has type of pointer to five elements integer array ( int (*)[5])
We have a function named func1 that accepts a pointer to an int that is, its parameter is int*. Now lets see what is happening in each of the cases.
Case I
In this case we have func1(arr);
Here the variable arr is an array of size 4 with elements of type int (which is int [4]) but it decays to a pointer to the first element(which is int*) due to type decay.
So when you wrote,
func1(arr);
essentially you're passing an int* as an argument which matches with the type of the function parameter.
Case II
In this case we have func1(&arr);
Here &arr means a pointer to an array of size 4 with elements of type int.(which is int (*)[4]).
Now when you wrote,
func1(&arr);
essentially you're passing an int (*)[4] as an argument to the function func1. But note that the parameter of func1 is of type int*. So there is mismatch in type of argument and parameter.
Now in the screenshot you posted, the program seems to be compiling and executed successfully. But note that since in the second case there is mismatch in argument and parmeter type you will get undefined behavior.
Undefined behavior means anything1 can happen including but not limited to the program giving your expected output. But never rely on the output of a program that has undefined behavior.
1For a more technically accurate definition of undefined behavior see this where it is mentioned that: there are no restrictions on the behavior of the program.
#include <iostream>
template <class T>
void call_1(T& in){
printf("%d\n", sizeof(in)); // 12
}
template <int N>
void call_2(int (&in)[N]){
printf("%d\n", sizeof(in)); // 12
}
int main(){
int a[] = {1,2,3};
call_1(a);
call_2(a);
return 0;
}
I have a few questions based on the code snippet above:
1- is call_1 and call_2 both valid ways to pass an entire array by reference?
2- why is T& necessary to have in call_1. If & is omitted it passes a pointer to
the first element of array. Why doesn't T& pass a reference to the first element of the array and instead it references the entire array?
3- Why can't I write template <T N> in call_2 an avoid hard coding int? is this
not a valid syntax?
1. Yes, both are valid.
2. The type of a is int[3]. But arrays in most rvalue contexts, such as when passing by-value, decay to a pointer. The decaying doesn't happen when passing by-reference, so you end up with int(&)[3].
3. Of course you can, but the type of the array dimension is not T, but size_t.
So it should be like this:
template <typename T, std::size_t N>
void call_2(T (&in)[N]){
printf("%d\n", sizeof(in)); // 12
}
1- is call_1 and call_2 both valid ways to pass an entire array by reference?
Yes. 1 also accepts non-array arguments.
2- why is T& necessary to have in call_1
Because without T& you won't have a type for the parameter.
Why doesn't T& pass a reference to the first element of the array and instead it references the entire array?
That's just not how the language works. When a reference is deduced from an array argument, it is deduced to be a reference to an array.
3- Why can't I write template <T N> in call_2 an avoid hard coding int? is this not a valid syntax?
Because you haven't told to the compiler what T is. There's technically no need to hard code the type of the length of the array. You can make it a template parameter like this:
template <class T, T N>
But typically hard-conding it isn't a problem. Note that conventionally, std::size_t is used rather than int.
printf("%d\n", sizeof(in));
%d is not the correct format specifier for std::size_t, so the behaviour of the program is undefined. I recommend using iostreams instead. It's much easier to avoid undefined behaviour with iostreams.
I am playing around with pointers, arrays of pointers, and arrays.
I created a struct called Fraction and tried passing an array of Fraction into a function that takes an array of Fraction pointers. I get the error:
Error 1 error C2664: 'void display(Fraction *[],int)' : cannot convert
argument 1 from 'Fraction (*)[10]' to 'Fraction *[]'
I understand that this does not work, but what you you call each of these? Fraction(*)[] and Fraction*[]. for example: int[] is an array of integers, and an int* is an integer pointer. The above code, looks identical to me aside from the parenthesis around the *.
I do not need to fix this error I just simply want to understand the differences between the two, seemingly similar structures.
Cause of the error
The parameter Fraction *fracArr[] expects an array of pointers to fractions.
You have defined Fraction farry[max_size]; meaning that farry is an array of fractions.
When you call the function providing &farry as first argument, you are trying to take a pointer to an array (Fraction (*)[10]) instead of an array of pointers (Fraction *[]). Therefore the mismatch error.
Solution
If your goal is to work with an array of fractions, just change your function as follows:
void display(Fraction fracArr[], int fracCounter){
for (int i = 0; i < fracCounter; i++){
cout << fracArr[i].num << "/" << fracArr[i].den << endl;
}
}
and call it with display(farry, fracCounter);.
Additional remarks:
More generally, an argument of type array of unknown size T arg[] is passed as a pointer T *arg pointing to the first element.
Defining your argument Fraction *arg[] or Fraction **arg would result in the same code. The [] just hides this technical detail and make the intent clearer (i.e. working with an array of pointers vs. working with a pointer to pointer)
Fraction*[] is an array of Fraction* (an array of pointers). Fraction(*)[] is a pointer to Fraction[] (pointer to an array). The difference is that parentheses isolate the "pointer" from the Fraction, because otherwise the two would bind to each other and give you a different type than intended.
Mechanically, a * or a & would much rather bind to a type name than be isolated and represent the entire thing, so you have to use parentheses to isolate it from the element type. This is also true when declaring function pointers: int*(int, int) is a function that takes two ints and returns an int*, while int(*)(int, int) is a pointer to a function that takes two ints and returns an int.
Consider this simple program:
#include <iostream>
#include <typeinfo>
struct Type {};
// 1: Array of Type*.
void func(Type *arr [3]) {
std::cout << "Type* array.\n"
<< typeid(arr).name() << "\n\n";
}
// 2: Array of Type&.
// Illegal.
// void func(Type &arr [3]) {
// std::cout << "Type& array.\n"
// << typeid(arr).name() << "\n\n";
// }
// 3: Pointer to array of Type.
void func(Type (*arr) [3]) {
std::cout << "Pointer to Type array.\n"
<< typeid(arr).name() << "\n\n";
}
// 4: Reference to array of Type.
void func(Type (&arr) [3]) {
std::cout << "Reference to Type array.\n"
<< typeid(arr).name() << "\n\n";
}
int main() {
// Array of Type.
Type t_arr[3] = {};
// Array of Type*.
Type* tp_arr[3] = { &t_arr[0], &t_arr[1], &t_arr[2] };
// Array of Type&.
// Illegal.
// Type& tr_arr[3] = { t_arr[0], t_arr[1], t_arr[2] };
std::cout << "Type[3]: " << typeid(t_arr).name() << "\n\n";
func(t_arr); // Calls #4.
func(&t_arr); // Calls #3.
func(tp_arr); // Calls #1.
}
Depending on the compiler used, it'll output either mangled or unmangled types for arr, and the output shows that all three are different types:
// MSVC:
Type[3]: struct Type [3]
Reference to Type array.
struct Type [3]
Pointer to Type array.
struct Type (*)[3]
Type* array.
struct Type * *
// GCC:
Type[3]: A3_4Type
Reference to Type array.
A3_4Type
Pointer to Type array.
PA3_4Type
Type* array.
PP4Type
This syntax is a bit wonky if you're not used to it, and can be somewhat easy to mistype, so it may be a good idea to make a type alias if you need to use it.
// Array.
typedef Type Type_arr_t[3];
// Pointer.
typedef Type (*Type_arr_ptr_t)[3];
// Reference.
typedef Type (&Type_arr_ref_t)[3];
// ...
// Without typedefs.
Type arr [3];
Type (*arr_p)[3] = &arr;
Type (&arr_r)[3] = arr;
// With typedefs.
Type_arr_t arr2;
Type_arr_ptr_t arr2_p = &arr2;
Type_arr_ref_t arr2_r = arr2;
This is extremely useful when declaring functions that return pointers or references to arrays, because they look silly without typedefs, and are really easy to get wrong and/or forget the syntax for.
typedef Type (*Type_arr_ptr_t)[3];
typedef Type (&Type_arr_ref_t)[3];
// Without typedefs.
Type (*return_ptr())[3];
Type (&return_ref())[3];
// With typedefs.
Type_arr_ptr_t return_ptr_2();
Type_arr_ref_t return_ref_2();
For more information regarding how to parse something like this, see the clockwise spiral rule.
Note: When an array is passed by value as a function parameter, and in many other situations (specifically, in any situation where an array isn't expected, but a pointer is), type and dimension information is lost, and it is implicitly converted to a pointer to the first element of the array; this is known as the array decaying into a pointer. This is demonstrated in func(Type*[3]) above, where the compiler takes a parameter type of Type*[3], an array of Type*, and replaces it with Type**, a pointer to Type*; the [3] is lost, and replaced with a simple *, because functions can take pointers but not arrays. When func() is called, the array will decay due to this. Due to this, the following signatures are treated as identical, with the parameter being Type** in all three.
void func(Type*[3]);
void func(Type*[] ); // Dimension isn't needed, since it'll be replaced anyways.
void func(Type** );
This is done because it's more efficient than trying to pass the entire array by value (it only needs to pass a pointer, which easily fits inside a single register, instead of trying to load the entire thing into memory), and because encoding the array type into the function's parameter list would remove any flexibility from the function regarding the size of the array it can take (if a function were to take Type[3], then you couldn't pass it a Type[4] or a Type[2]). Due to this, the compiler will silently replace a Type[N] or Type[] with a Type*, causing the array to decay when passed. This can be avoided by specifically taking a pointer or reference to the array; while this is as efficient as letting the array decay (the former because it still just passes a pointer, the latter because most compilers implement references with pointers), it loses out on flexibility (which is why it's usually paired with templates, which restore the flexibility, without removing any of the strictness).
// Will take any pointer to a Type array, and replace N with the number of elements.
// Compiler will generate a distinct version of `func()` for each unique N.
template<size_t N>
void func(Type (*)[N]);
// Will take any reference to a Type array, and replace N with the number of elements.
// Compiler will generate a distinct version of `func()` for each unique N.
template<size_t N>
void func(Type (&)[N]);
Note, however, that C doesn't have the luxury of templates, and thus any code that is intended to work with both languages should either use the C idiom of passing a "size" parameter along with the array, or be written specifically for a certain size of array; the former is more flexible, while the latter is useful if you will never need to take an array of any other size.
void func1(Type *arr, size_t sz);
void func2(Type (*arr)[3]);
Also note that there are situations where an array won't decay into a pointer.
// Example array.
Type arr[3];
// Function parameter.
void func(Type arr[3]);
void func(Type (*arr)[3]);
void func(Type (&arr)[3]);
// Function template parameter.
template<typename T>
void temp(T t);
// Class template parameter.
template<typename T>
struct S { typedef T type; };
// Specialised class template parameter.
template<typename T> struct S2;
template<typename T, size_t Sz>
struct S2<T[Sz]> { typedef T type[Sz]; };
func(arr); // C: arr decays into Type*.
// C++: arr either binds to a Type(&)[3], or decays into Type*.
// If both are available, causes error due to ambiguous function call.
func(&arr); // C/C++: No decay, &arr is Type(*)[3].
sizeof(arr); // C/C++: No decay, evaluates to (sizeof(Type) * 3).
alignof(arr); // C/C++: No decay, evaluates to alignof(Type).
decltype(arr); // C++: No decay, evaluates to Type[3].
typeid(arr); // C++: No decay, evaluates to a std::type_info for Type[3].
for (Type& t : arr); // C++: No decay, ranged-based for accepts arrays.
temp(arr); // C++: arr decays to Type* during function template deduction.
temp<Type[3]>(arr); // C++: No decay, deduction isn't required.
// For class templates, deduction isn't performed, so array types used as template parameters
// don't decay.
S<Type[3]>::type; // C++: No decay, type is Type[3].
S2<Type[3]>::type; // C++: No decay, type is Type[3].
// String literals are arrays, too.
decltype("Hello."); // C++: No decay, evaluates to const char[7].
char c_arr[] = "Hello."; // C/C++: No decay, c_arr is a local array, of type char[7],
// containing copy of "Hello."
const char* c_ptr = "Hello."; // C/C++: const char[7] "Hello." is stored in read-only
// memory, and ptr points to it.
// There may be other cases in which arrays don't decay, which I'm currently not aware of.
So, in short, while, say, Type[3] is an array type, and Fraction*[5] is an array type, there are cases where a declaration of the two will be silently replaced with a Type* or Fraction**, respectively, by the compiler, and where type and dimension information will be lost due to this; this loss is known as array decay or array-to-pointer decay.
Thanks go to juanchopanza for reminding me to mention array-to-pointer decay.
This is one of these places where the compiler outputting a raw type versus a parameter declaration causes a little confusion. If you re-insert the variable names, the comparison is now between:
Fraction (*farray)[10]
and:
Fraction *farray[]
At this point the error becomes obvious if you are willing to accept that declarations have a precedence just like regular expressions.
According to C/C++'s precedence table, [] as the array index operator binds more tightly than unary * the pointer de-reference operator.
If you apply this same rule to the declarations, the second becomes an array of pointers, while the first one has the "pointer" bound more tightly due to the parentheses, therefore it is a pointer to an array.
I'm a C++ newbie. I want to determine the size of string array in C++. Following advice on this forum, I've created a template to give me the size of the array. The releavant code is
virtual std::string _verify_list_or_string(std::string obj[]) {
std::cout << "debug " << sizeof_array(std::string, sizeof(obj)) << std::endl;
The template I'm using is
template <typename T, std::size_t N>
std::size_t sizeof_array( T (&)[N] ) {
return N;
}
I get a compile error: "error: expected primary-expression before ‘,’"
What am I doing wrong?
The problem is that, despite the [], the function argument is a pointer and not an array. You can only pass an array to a function by reference, and then it must be a reference to a specific size of array - the size is part of the array's type.
If you need the size of an array that's been passed as a pointer, then some options are:
make the function itself a template, like your sizeof_array, so that the array is passed by reference and the size available as a template parameter;
make the function a template taking an iterator range in two arguments, so it can be used with any kind of sequence (including arrays);
pass the size of the array as a second argument;
pass a class type like std::array or std::vector with an interface that can give you the size.
The error is because you're passing nonsense to sizeof_array. If obj were an array, then you'd do sizeof_array(obj), and the template arguments would be inferred from the type of obj. But it isn't an array, so that won't work either.
The problematic expression is sizeof_array(std::string, sizeof(obj)): std::string isn't an expression but a type. Fixing that expression won't help, though: the argument obj isn't a suitable argument for sizeof_array() because it has type std::string* while sizeof_array() takes an array reference as argument.
Once the array has decayed into a pointer there is no way to recover the array size. If you want to automatically determine the size of an array in a function, you need to pass the array by reference, e.g.:
template <int Size>
std::string _verify_list_or_string(std::string (&obj)[Size]) {
std::cout << "debug " << sizeof_array(obj) << '\n';
return _verify_list_or_string(obj, Size);
}
virtual std::string _verify_list_or_string(std::string* obj, int Size) {
...
}
Note that the primary entry point was turned into a template which can't be virtual. Thus, it forwards to a virtual function taking a pointer to the array and its size.
Your function takes one parameter:
std::size_t sizeof_array( T (&)[N] )
// ^^^^^^^^^^
But you're passing two. Moreover, one of them - type:
sizeof_array(std::string, sizeof(obj))
// ^^^^^^^^^^^^ ^^^^^^^^^^^
You'd probably want to pass an obj variable. But the problem is that obj is not an array here. So, you can't get size of obj this way.
That's not how you invoke a template function... arguments, not template parameters go inside the function-call parentheses. That function is designed to infer the template parameters. If inference wasn't possible, you'd call like this:
std::string ary[6];
sizeof_array<std::string, 6>(ary);
Since inference is possible, the compiler will figure out the template parameters if you just pass the argument:
sizeof_array(ary);
Unfortunately for you, that template function only works on arrays, but you have a pointer. There's no size information for you to get to.
The following code returns the size of a stack-allocated array:
template<typename T, int size>
int siz(T (&) [size])
{
return size;
}
but I can't wrap my head around the syntax.
Especially the T (&) [size] part...
but I can't wrap my head around the syntax. Especially the T (&) [size] part...
That part is a reference to an array. There is the "right-left rule" for deciphering any C and C++
declarations.
Because function templates deduce template argument types from the supplied function arguments what this function template does is deduce the type and element count of an array and return the count.
Functions can't accept array types by value, rather only by pointer or reference. The reference is used to avoid the implicit conversion of an array to the pointer to its first element (aka, array decay):
void foo(int*);
int x[10];
int* p = x; // array decay
foo(x); // array decay again
Array decay destroys the original type of the array and hence the size of it gets lost.
Note, that because it is a function call in C++03 the return value is not a compile time constant (i.e. the return value can't be used as a template argument). In C++11 the function can be marked with constexpr to return a compile time constant:
template<typename T, size_t size>
constexpr size_t siz(T(&)[size]) { return size; }
To get the array element count as a compile time constant in C++03 a slightly different form may be used:
template<class T, size_t size>
char(&siz(T(&)[size]))[size]; // no definition required
int main()
{
int x[10];
cout << sizeof siz(x) << '\n';
double y[sizeof siz(x)]; // use as a compile time constant 10
}
In the above it declares a function template with the same reference-to-an-array argument, but with the return value type of char(&)[size] (this is where the "right-left rule" can be appreciated). Note that the function call never happens at run-time, this is why the definition of function template siz is unnecessary. sizeof siz(x) is basically saying "what would be the size of the return value if siz(x) were called".
The old C/C++ way of getting the element count of an array as a compile time constant is:
#define SIZ(arr) (sizeof(arr) / sizeof(*(arr)))
T (&) [size] is a reference to an array. It needs to be a reference because the following program is not legal:
#include <iostream>
int sz(int *) { std::cout << "wtf?" << std::endl; return 0; }
int sz(int [4]) { std::cout << "4" << std::endl; return 0; }
int main() {
int test[4];
sz(test);
}
This program fails to compile with:
test.cc: In function ‘int sz(int*)’:
test.cc:6:5: error: redefinition of ‘int sz(int*)’
test.cc:3:5: error: ‘int sz(int*)’ previously defined here
because int sz(int [4]) is identical to int sz(int *).
The parenthesis are required to disambiguate here because T& [size] looks like an array of references which is otherwise illegal.
Normally if the parameter wasn't anonymous you would write:
template<typename T, int size>
int sz(T (&arr) [size])
To give the array the name arr. In this instance though all your example code cared about was the deduced size and hence the anonymous argument avoids warnings about unused arguments.
It´s an function which becomes a typename (templates can be used with different typenames) and a size from the outside. It then returns this size.
Stack functions often use size, which is an integer number which shows you the size of the stack-size you request with this function. The & tests just which size of stack T is meant.