In the following code
#include<iostream>
template<typename T,size_t N>
void cal_size(T (&a)[N])
{
std::cout<<"size of array is: "<<N<<std::endl;
}
int main()
{
int a[]={1,2,3,4,5,6};
int b[]={1};
cal_size(a);
cal_size(b);
}
As expected the size of both the arrays gets printed. But how does N automatically gets initialized to the correct value of the array-size (arrays are being passed by reference)? How is the above code working?
N does not get "initialized" to anything. It is not a variable. It is not an object. N is a compile-time constant. N only exists during compilation. The value of N as well as the actual T is determined by the process called template argument deduction. Both T and N are deduced from the actual type of the argument you pass to your template function.
In the first call the argument type is int[6], so the compiler deduces that T == int and N == 6, generates a separate function for that and calls it. Let's name it cal_size_int_6
void cal_size_int_6(int (&a)[6])
{
std::cout << "size of array is: " << 6 << std::endl;
}
Note that there's no T and no N in this function anymore. Both were replaced by their actual deduced values at compile time.
In the first call the argument type is int[1], so the compiler deduces that T == int and N == 1, generates a separate function for that as well and calls it. Let's name it cal_size_int_1
void cal_size_int_1(int (&a)[1])
{
std::cout << "size of array is: " << 1 << std::endl;
}
Same thing here.
Your main essentially translates into
int main()
{
int a[]={1,2,3,4,5,6};
int b[]={1};
cal_size_int_6(a);
cal_size_int_1(b);
}
In other words, your cal_size template gives birth to two different functions (so called specializations of the original template), each with different values of N (and T) hardcoded into the body. That's how templates work in C++.
It works because the type of a is "array of length 6 of int" and the type of b is "array of length 1 of int". The compiler knows this, so it can call the correct function. In particular, the first call calls the template instance cal_size<6>() and the second call calls cal_size<1>(), since those are the only template instantiations which match their respective arguments.
If you attempted to call an explicit template instance, it would only work if you got the size right, otherwise the arguments wouldn't match. Consider the following:
cal_size(a); // ok, compiler figures out implicitly that N=6
cal_size<int, 6>(a); // also ok, same result as above
cal_size<int, 5>(a); // ERROR: a is not of type "array of length 5 of int"
when you declare int a[] = {1,2,3} it is the same as (or will be rewritten as) int a[3] = {1,2,3} since the templated function is receiving argument in form of T a[N], then N will have value of 3.
Related
What does the parameter printArray(int (&a)[n][m]) mean? Why are the parentheses necessary and why does only 1 value need to be provided to the printArray function? how does the function know n and m when called?
template <size_t n, size_t m>
void printArray(int (&a)[n][m]) {
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
cout << a[i][j] << " ";
}
cout << endl;
}
}
int main(int argc, char* argv[])
{
cout << "Example I:" << endl;
int ab[2][5];
printArray(ab);
cout << "Example II:" << endl;
int b[2][5] = {{1, 2, 3}};
printArray(b);
cout << "Example III:"<< endl;
int c[][5] = {1, 2, 3, 4, 5, 6, 7};
printArray(c);
cout << "Example IV:" << endl;
int d[][5] = {{1, 2, 3, 4}, {5, 6}, {7}};
printArray(d);
}
What does the parameter printArray(int (&a)[n][m]) mean?
It means that a is a reference to an object of type int[n][m]. int[n][m] is an array of m objects of type int[n]. int[n] is an array of n objects of type int. So, a is a reference to a 2d array with dimensions n and m.
n and m are template arguments that were declared in template <size_t n, size_t m>. The type of both argument is size_t, which is an integer type.
Why are the parentheses necessary
Because the &-token binds to left. int& is a reference to int. int& a[n] would syntactically mean that a is an array of references (such arrays are not allowed though). The parentheses are used to disambiguate whether the &-token declares an (array of) reference to int, or a reference to an array.
and why does only 1 value need to be provided to the printArray function?
There is exactly one argument to the function: a. If you pass a value that can be bound to an array reference of appropriate type, then it works. In all your examples the arguments are 2d arrays of integers, so they are correct.
how does the function know n and m when called?
The compiler knows because size of an array is part of the type of the array. And because of template argument deduction. When a template argument is not specified explicitly, it may be deduced from the arguments of the function. In this case, if you pass an argument of type int[2][5], then n is deduced to be 2 and m is deduced to be 5.
You could even add a template type argument and let that be deduced:
template <size_t n, size_t m, typename T>
void printArray(T (&a)[n][m])
T would be deduced to be int, if you were to pass a 2d array of integers.
if an array of references isn't allowed, why can't the compiler deduce that the parenthesis is not necessary.
If int &a[n] would mean a reference to an array, because there can't be arrays of references, then it would confuse programmers that int *a[n] is not a pointer to an array, because there can be arrays of pointers.
Besides, this would complicate the language by adding a special case for references that is unnecessary.
why isn't the form: printArray(int[n][m] &a)
More simply, why can't arrays be declared by int[n] a instead of int a[n]. Because the latter syntax was chosen by (presumably Dennis Ritchie when he was) designer of the C language.
It's simply taking an array by reference. As far as usage of the passed array goes inside the function, you typically will write exactly the same code as you would have otherwise. The only difference is that it preserves its array type.
When you pass arrays "by value" to a function it's actually decayed into a pointer to its first element. Passing it by reference (or by address) prevents this decay in this usage.
In fact, even if you specify an extent for the first dimension in the parameter, the compiler will ignore it if the array is passed "by value".
This is a template programming trick to deduce the extent of the first dimension, which would otherwise by lost if you had written:
template<size_t m>
void printArray(int a[][m]) {...}
Thus the alternative would have been something less tidy like:
template<size_t m>
void printArray(int a[][m], size_t n) {...}
The parenthesis are required because int &a[n][m] is a 2D array of references, which is illegal because you cannot make an array of reference. The parenthesis in int (&a)[n][m] makes this "reference to nxm array of int".
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.
This is an exercise from C++ Primer 5th edition, which goes:
Exercise 6.24: Explain the behavior of the following function. If there are problems in the code, explain what they are and how you might fix them.
void print(const int ia[10])
{
for (size_t i = 0; i != 10; ++i)
cout << ia[i] << endl;
}
I can not find any problem in the codes. What is the point of this exercise?
The general problem is that in C++ declaration syntax, array types in function parameter declarations mean something non-intuitive: A parameter declared as T[] or as T[10] or as T[1279] is actually declared as T* – all these parameter declarations are identical.*
Remember that there are no prvalues of array type in C++, and so array types cannot be function parameter or return types. (When used as a prvalue, an array decays to a pointer to its first element.)
Therefore, your function declaration is actually (with T = const int):
void print(const int *);
This parameter type plays well with the array-to-pointer decay, but it is now clear that you can pass any pointer to int to this function, and the correctness of the function execution cannot be determined from the function definition alone.
*) It's a bit more complicated in C99.
On a side note, array glvalues are perfectly fine, as is the following function which has a parameter whose type is "reference to array":
void print10(const int (&a)[10])
{
for (auto i : a) { std::cout << i << "\n"; }
}
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.